[
  {
    "path": ".github/workflows/tests.yaml",
    "content": "name: Tests\non: [push]\njobs:\n  tests:\n    name: ${{ matrix.peewee-backend }} - ${{ matrix.python-version }}\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n    services:\n      mysql:\n        image: mariadb:latest\n        env:\n          MYSQL_ROOT_PASSWORD: peewee\n          MYSQL_DATABASE: peewee_test\n        ports:\n          - 3306:3306\n      postgres:\n        image: postgres\n        env:\n          POSTGRES_USER: postgres\n          POSTGRES_PASSWORD: peewee\n          POSTGRES_DB: peewee_test\n        ports:\n          - 5432:5432\n    strategy:\n      fail-fast: false\n      matrix:\n        python-version: [3.9, \"3.11\", \"3.13\", \"3.14\"]\n        peewee-backend:\n          - \"sqlite\"\n          - \"postgresql\"\n          - \"mysql\"\n        include:\n          - python-version: \"3.8\"\n            peewee-backend: sqlite\n          - python-version: \"3.12\"\n            peewee-backend: sqlite\n          - python-version: \"3.12\"\n            peewee-backend: psycopg3\n          - python-version: \"3.13\"\n            peewee-backend: psycopg3\n          - python-version: \"3.14\"\n            peewee-backend: psycopg3\n          - python-version: \"3.13\"\n            peewee-backend: cysqlite\n          - python-version: \"3.13\"\n            peewee-backend: cockroachdb\n    steps:\n      - uses: actions/checkout@v4\n      - uses: actions/setup-python@v5\n        with:\n          python-version: ${{ matrix.python-version }}\n      - name: deps\n        env:\n          PGUSER: postgres\n          PGHOST: 127.0.0.1\n          PGPASSWORD: peewee\n        run: |\n          sudo apt-get install libsqlite3-dev\n          pip install setuptools psycopg2-binary cython pymysql 'apsw' mysql-connector sqlcipher3 pysqlite3 'psycopg[binary]' gevent\n          pip install greenlet aiosqlite aiomysql asyncpg pydantic\n          python setup.py build_ext -i\n          psql peewee_test -c 'CREATE EXTENSION hstore;'\n      - name: crdb\n        if: ${{ matrix.peewee-backend == 'cockroachdb' }}\n        run: |\n          wget -qO- https://binaries.cockroachdb.com/cockroach-v22.2.6.linux-amd64.tgz | tar xz\n          ./cockroach-v22.2.6.linux-amd64/cockroach start-single-node --insecure --background\n          ./cockroach-v22.2.6.linux-amd64/cockroach sql --insecure -e 'create database peewee_test;'\n      - name: cysqlite\n        if: ${{ matrix.peewee-backend == 'cysqlite' || matrix.peewee-backend == 'sqlite' }}\n        run: |\n          pip install -e 'git+https://github.com/coleifer/cysqlite#egg=cysqlite'\n      - name: runtests ${{ matrix.peewee-backend }} - ${{ matrix.python-version }}\n        env:\n          PEEWEE_TEST_BACKEND: ${{ matrix.peewee-backend }}\n          PGUSER: postgres\n          PGHOST: 127.0.0.1\n          PGPASSWORD: peewee\n          CI: 1\n        run: python runtests.py --mysql-user=root --mysql-password=peewee -s -v2\n"
  },
  {
    "path": ".github/workflows/wheels.yaml",
    "content": "name: Build wheels\non:\n push:\n   tags:\n     - \"[0-9]+.[0-9]+.[0-9]+\"\n     - \"[0-9]+.[0-9]+.[0-9]+-**\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: actions/setup-python@v5\n        with:\n          python-version: \"3.12\"\n\n      - name: Install build tools\n        run: |\n          python -m pip install -U pip\n          pip install setuptools build\n\n      - name: Build sdist and wheel\n        env:\n          NO_SQLITE: 1\n        run: |\n          python -m build .\n\n      - uses: actions/upload-artifact@v4\n        with:\n          name: package\n          path: dist/peewee*\n\n  publish:\n    needs: [build]\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/download-artifact@v4\n        with:\n          name: package\n          path: dist\n          merge-multiple: true\n\n      - uses: pypa/gh-action-pypi-publish@release/v1\n        with:\n          user: __token__\n          password: ${{ secrets.PYPI_API_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.pyc\nbuild\nprof/\ndocs/_build/\nplayhouse/*.c\nplayhouse/*.h\nplayhouse/*.so\nplayhouse/tests/peewee_test.db\n.idea/\nMANIFEST\npeewee_test.db\nclosure.so\nlsm.so\nregexp.so\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "version: 2\npython:\n  install:\n  - requirements: docs/requirements.txt\n\nbuild:\n  os: ubuntu-22.04\n  tools:\n    python: \"3.11\"\n\nsphinx:\n  configuration: docs/conf.py\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: python\npython:\n  - 2.7\n  - 3.4\n  - 3.5\n  - 3.6\nenv:\n  - PEEWEE_TEST_BACKEND=sqlite\n  - PEEWEE_TEST_BACKEND=postgresql\n  - PEEWEE_TEST_BACKEND=mysql\nmatrix:\n  include:\n    - python: 3.7\n      dist: xenial\n      env: PEEWEE_TEST_BACKEND=sqlite\n    - python: 3.7\n      dist: xenial\n      env: PEEWEE_TEST_BACKEND=postgresql\n    - python: 3.7\n      dist: xenial\n      env: PEEWEE_TEST_BACKEND=mysql\n    - python: 3.8\n      dist: xenial\n    - python: 3.7\n      dist: xenial\n      env:\n      - PEEWEE_TEST_BUILD_SQLITE=1\n      - PEEWEE_CLOSURE_EXTENSION=/usr/local/lib/closure.so\n      - LSM_EXTENSION=/usr/local/lib/lsm.so\n      before_install:\n        - sudo apt-get install -y tcl-dev\n        - ./.travis_deps.sh\n        - sudo ldconfig\n      script: \"python runtests.py -v2\"\n    - python: 3.7\n      dist: xenial\n      env:\n      - PEEWEE_TEST_BACKEND=cockroachdb\n      before_install:\n        - wget -qO- https://binaries.cockroachdb.com/cockroach-v20.1.1.linux-amd64.tgz | tar xvz\n        - ./cockroach-v20.1.1.linux-amd64/cockroach start --insecure --background\n        - ./cockroach-v20.1.1.linux-amd64/cockroach sql --insecure -e 'create database peewee_test;'\n  allow_failures:\naddons:\n  postgresql: \"9.6\"\n  mariadb: \"10.3\"\nservices:\n  - postgresql\n  - mariadb\ninstall: \"pip install psycopg2-binary Cython pymysql apsw mysql-connector\"\nbefore_script:\n  - python setup.py build_ext -i\n  - psql -c 'drop database if exists peewee_test;' -U postgres\n  - psql -c 'create database peewee_test;' -U postgres\n  - psql peewee_test -c 'create extension hstore;' -U postgres\n  - mysql -e 'drop user if exists travis@localhost;'\n  - mysql -e 'create user travis@localhost;'\n  - mysql -e 'drop database if exists peewee_test;'\n  - mysql -e 'create database peewee_test;'\n  - mysql -e 'grant all on *.* to travis@localhost;' || true\nscript: \"python runtests.py\"\n"
  },
  {
    "path": ".travis_deps.sh",
    "content": "#!/bin/bash\n\nsetup_sqlite_deps() {\n  wget https://www.sqlite.org/src/tarball/sqlite.tar.gz\n  tar xzf sqlite.tar.gz\n  cd sqlite/\n  export CFLAGS=\"-DSQLITE_ENABLE_FTS3 \\\n    -DSQLITE_ENABLE_FTS3_PARENTHESIS \\\n    -DSQLITE_ENABLE_FTS4 \\\n    -DSQLITE_ENABLE_FTS5 \\\n    -DSQLITE_ENABLE_JSON1 \\\n    -DSQLITE_ENABLE_LOAD_EXTENSION \\\n    -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \\\n    -DSQLITE_TEMP_STORE=3 \\\n    -DSQLITE_USE_URI \\\n    -O2 \\\n    -fPIC\"\n  export PREFIX=\"/usr/local\"\n  LIBS=\"-lm\" ./configure \\\n    --disable-tcl \\\n    --enable-shared \\\n    --enable-tempstore=always \\\n    --prefix=\"$PREFIX\"\n  make && sudo make install\n\n  cd ext/misc/\n\n  # Build the transitive closure extension and copy shared library.\n  gcc -fPIC -O2 -lsqlite3 -shared closure.c -o closure.so\n  sudo cp closure.so /usr/local/lib\n\n  # Build the lsm1 extension and copy shared library.\n  cd ../lsm1\n  export CFLAGS=\"-fPIC -O2\"\n  TCCX=\"gcc -fPIC -O2\" make lsm.so\n  sudo cp lsm.so /usr/local/lib\n}\n\nif [ -n \"$PEEWEE_TEST_BUILD_SQLITE\" ]; then\n  setup_sqlite_deps\nfi\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nTracking changes in peewee between versions.  For a complete view of all the\nreleases, visit GitHub:\n\nhttps://github.com/coleifer/peewee/releases\n\n## master\n\n[View commits](https://github.com/coleifer/peewee/compare/4.0.2...master)\n\n## 4.0.2\n\n* Remove all Python 2.x compatibility code.\n* Add streaming result cursors to pwasyncio module via `db.iterate(query)`.\n* Better serialization and deserialization of datetimes and binary data in the\n  DataSet module. Previously binary data was encoded as base64, going forward\n  hex is the new default. For base64 specify `base64_bytes=True`.\n* Improvements to Postgres `BinaryJSONField`, support atomic removal of\n  sub-elements, as well as alternate helper for extracting sub-elements and\n  querying array length.\n* [Pydantic integration](https://docs.peewee-orm.com/en/latest/peewee/orm_utils.html#module-playhouse.pydantic_utils)\n\n[View commits](https://github.com/coleifer/peewee/compare/4.0.1...4.0.2)\n\n## 4.0.1\n\n* Ensure `gr_context` is set on greenlet in `greenlet_spawn` so that\n  contextvars will be operable in sync handlers.\n* Removed `SqliteExtDatabase` (it basically served no purpose in 4.0). Use\n  `SqliteDatabase` instead.\n* Moved driver and extension-specific pooled implementations into the\n  corresponding extension module rather than putting all into `playhouse.pool`.\n* Restore custom `dumps` option for postgres JSON fields.\n* Major docs rewrite / reorganization.\n\n[View commits](https://github.com/coleifer/peewee/compare/4.0.0...4.0.1)\n\n## 4.0.0\n\n* Adds preliminary support for `asyncio` via a new playhouse extension. See\n  [the documentation](http://docs.peewee-orm.com/en/latest/peewee/asyncio.html)\n  for details.\n* `PostgresqlDatabase` can use `psycopg` (psycopg3) if it is installed. If both\n  psycopg2 and psycopg3 are installed, Peewee will prefer psycopg2, but this\n  can be controlled by specifying `prefer_psycopg3=True` in the constructor.\n  Same applies to `PostgresqlExtDatabase`.\n* `Psycopg3Database` class has been moved to `playhouse.postgres_ext` and is\n  now just a thin wrapper around `PostgresqlExtDatabase`.\n* Postgres JSON operations no longer dump and try to do minimal casts, instead\n  relying on the driver-provided `Json()` wrapper(s).\n* Adds new `ISODateTimeField` for Sqlite that encodes datetimes in ISO format\n  (more friendly when db is shared with other tools), and also properly reads\n  back UTC offset info.\n* Remove `playhouse.sqlite_ext.ClosureTable` implementation.\n* Add a `Model.dirty_field_names` attribute that is safe for membership\n  testing, since testing `x in dirty_fields` returns True if one or more field\n  exists due to operator overloads returning a truthy Expression object.\n  Refs #3028.\n* Removal of Cython `_sqlite_ext` extension. The C implementations of the FTS\n  rank functions are moved to `sqlite_udf`. Most of the remaining functionality\n  is moved to `playhouse.cysqlite_ext` which supports it natively.\n\nMigrating `CSqliteExtDatabase` usage:\n\nYou can either use `sqlite_ext.SqliteExtDatabase` or try the new\n`cysqlite_ext.CySqliteDatabase` if you want all the old functionality and are\nwilling to try a new driver.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.19.0...4.0.0)\n\n## 3.19.0\n\n* Move to new build system using pyproject and github actions.\n* No longer build and ship the Sqlite C extensions by default. Users who prefer\n  to use those can install via the sdist `pip install peewee --no-binary :all:`.\n\nRationale about the Sqlite C extensions -- I've started shipping pysqlite3 as a\nstatically-linked, self-contained binary wheel. This means that when using\nPeewee with the statically-linked pysqlite3, you can end up in a funny\nsituation where the peewee Sqlite extensions are linked against the system\nlibsqlite3, and the pysqlite driver has it's own Sqlite embedded, which does\nnot work.\n\nIf you are using the system/standard-lib sqlite3 module then the extension\nworks properly, because everything is talking to the same `libsqlite3`.\n\nSimilarly if you built pysqlite3 to link against the system `libsqlite3`\neverything also works correctly, though this is not \"wheel-friendly\".\n\nSo in order to use the C extensions, you can install Peewee from the sdist and\ndo either of the following:\n\n```\n# Use system sqlite and standard-lib `sqlite3` module.\n$ pip install peewee --no-binary :all:\n\n# OR,\n# Use pysqlite3 linked against the system sqlite.\n$ pip install pysqlite3 peewee --no-binary :all:\n```\n\nI don't believe, besides myself, there were many people using these extensions\nso hopefully this change is not disruptive! Please let me hear about it if I'm\nmistaken.\n\nOther small changes:\n\n* When exporting / \"freezing\" binary data with the `playhouse.dataset` JSON\n  serializer, encode binary data as base64.\n\n## 3.18.3\n\n* Fix potential regex DoS vulnerability in FTS5 query validation code (#3005).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.18.2...3.18.3)\n\n## 3.18.2\n\nCython 3.1 removes some Python 2 stuff we referenced -- this resolves the\nissue. Couple other very minor fixes.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.18.1...3.18.2)\n\n## 3.18.1\n\n@pypa is such a bunch of clowns. I swear.\n\n![](https://media.charlesleifer.com/blog/photos/p1746027512.1307786.gif)\n\n[View commits](https://github.com/coleifer/peewee/compare/3.18.0...3.18.1)\n\n## 3.18.0\n\nThe behavior of `postgresql_ext.BinaryJSONField.contains()` has changed.\nPreviously, passing a string to this method would perform a JSON key exists\ncheck (`?` operator) instead of JSON contains (`@>` operator). As of 3.18.0,\nthis special-case has been **removed** and the `contains()` method always uses\nthe JSONB contains operator (`@>`). For the **old** behavior of checking\nwhether a key exists, use the `BinaryJSONField.has_key()` method. See #2984 for\ndiscussion.\n\n* Add options to URL-unquote user and password when using the `db_url` helpers,\n  see #2974 for discussion.\n* Support using `postgresql://` URLs when connecting to psycopg3.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.9...3.18.0)\n\n## 3.17.9\n\n* Fix incorrect handling of fk constraint name in migrator.\n* Fix test-only issue that can occur in Python 3.14a4.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.8...3.17.9)\n\n## 3.17.8\n\n* Fix regression in behavior of `delete_instance()` when traversing nullable\n  foreign-keys, #2952. Introduced in 3.17.6. **Recommended that you update**.\n* Fix bug where joins not cloned when going from join-less -> joined query,\n  refs #2941.\n\n## 3.17.7\n\n* Add db_url support for psycopg3 via `psycopg3://`.\n* Ensure double-quotes are escaped properly when introspecting constraints.\n* A few documentation-related fixes.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.6...3.17.7)\n\n## 3.17.6\n\n* Fix bug in recursive `model.delete_instance()` when a table contains\n  foreign-keys at multiple depths of the graph, #2893.\n* Fix regression in pool behavior on systems where `time.time()` returns\n  identical values for two connections. This adds a no-op comparable sentinel\n  to the heap to prevent any recurrence of this problem, #2901.\n* Ensure that subqueries inside `CASE` statements generate correct SQL.\n* Fix regression that broke server-side cursors with Postgres (introduced in\n  3.16.0).\n* Fix to ensure compatibility with psycopg3 - the libpq TransactionStatus\n  constants are no longer available on the `Connection` instance.\n* Fix quoting issue in pwiz that could generate invalid python code for\n  double-quoted string literals used as column defaults.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.5...3.17.6)\n\n## 3.17.5\n\nThis release fixes a build system problem in Python 3.12, #2891.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.4...3.17.5)\n\n## 3.17.4\n\n* Fix bug that could occur when using CASE inside a function, and one or more\n  of the CASE clauses consisted of a subquery. Refs #2873.\n  new fix in #2872 for regression in truthiness of cursor.\n* Fix bug in the conversion of TIMESTAMP type in Sqlite on Python 3.12+.\n* Fix for hybrid properties on subclasses when aliased (#2888).\n* Many fixes for SqliteQueueDatabase (#2874, #2876, #2877).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.3...3.17.4)\n\n## 3.17.3\n\n* Better fix for #2871 (extraneous queries when coercing query to list), and\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.2...3.17.3)\n\n## 3.17.2\n\n* Full support for `psycopg3`.\n* Basic support for Sqlite `jsonb`.\n* Fix bug where calling `list(query)` resulted in extra queries, #2871\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.1...3.17.2)\n\n## 3.17.1\n\n* Add bitwise and other helper methods to `BigBitField`, #2802.\n* Add `add_column_default` and `drop_column_default` migrator methods for\n  specifying a server-side default value, #2803.\n* The new `star` attribute was causing issues for users who had a field named\n  star on their models. This attribute is now renamed to `__star__`. #2796.\n* Fix compatibility issues with 3.12 related to utcnow() deprecation.\n* Add stricter locking on connection pool to prevent race conditions.\n* Add adapters and converters to Sqlite to replace ones deprecated in 3.12.\n* Fix bug in `model_to_dict()` when only aliases are present.\n* Fix version check for Sqlite native drop column support.\n* Do not specify a `reconnect=` argument to `ping()` if using MySQL 8.x.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.17.0...3.17.1)\n\n## 3.17.0\n\n* Only roll-back in the outermost `@db.transaction` decorator/ctx manager if\n  an unhandled exception occurs. Previously, an unhandled exception that\n  occurred in a nested `transaction` context would trigger a rollback. The use\n  of nested `transaction` has long been discouraged in the documentation: the\n  recommendation is to always use `db.atomic`, which will use savepoints to\n  properly handle nested blocks. However, the new behavior should make it\n  easier to reason about transaction boundaries - see #2767 for discussion.\n* Cover transaction `BEGIN` in the reconnect-mixin. Given that no transaction\n  has been started, reconnecting when beginning a new transaction ensures that\n  a reconnect will occur if it is safe to do so.\n* Add support for setting `isolation_level` in `db.atomic()` and\n  `db.transaction()` when using Postgres and MySQL/MariaDB, which will apply to\n  the wrapped transaction. Note: Sqlite has supported a similar `lock_type`\n  parameter for some time.\n* Add support for the Sqlite `SQLITE_DETERMINISTIC` function flag. This allows\n  user-defined Sqlite functions to be used in indexes and may be used by the\n  query planner.\n* Fix unreported bug in dataset import when inferred field name differs from\n  column name.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.16.3...3.17.0)\n\n## 3.16.3\n\n* Support for Cython 3.0.\n* Add flag to `ManyToManyField` to prevent setting/getting values on unsaved\n  instances. This is worthwhile, since reading or writing a many-to-many has no\n  meaning when the instance is unsaved.\n* Adds a `star()` helper to `Source` base-class for selecting all columns.\n* Fix missing `binary` types for mysql-connector and mariadb-connector.\n* Add `extract()` method to MySQL `JSONField` for extracting a jsonpath.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.16.2...3.16.3)\n\n## 3.16.2\n\nFixes a longstanding issue with thread-safety of various decorators, including\n`atomic()`, `transaction()`, `savepoint()`. The context-managers are\nunaffected. See #2709 for details.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.16.1...3.16.2)\n\n## 3.16.1\n\n* Add changes required for building against Cython 3.0 and set Cython\n  language-level to 3.\n* Ensure indexes aren't added to unindexed fields during introspection, #2691.\n* Ensure we don't redundantly select same PK in prefetch when using\n  PREFETCH_TYPE.JOIN.\n* In Sqlite migrator, use Sqlite's builtin DROP and RENAME column facilities\n  when possible. This can be overridden by passing `legacy=True` flag.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.16.0...3.16.1)\n\n## 3.16.0\n\nThis release contains backwards-incompatible changes in the way Peewee\ninitializes connections to the underlying database driver. Previously, peewee\nimplemented autocommit semantics *on-top* of the existing DB-API transactional\nworkflow. Going forward, Peewee instead places the DB-API driver into\nautocommit mode directly.\n\nWhy this change?\n\nPreviously, Peewee emulated autocommit behavior for top-level queries issued\noutside of a transaction. This necessitated a number of checks which had to be\nperformed each time a query was executed, so as to ensure that we didn't end up\nwith uncommitted writes or, conversely, idle read transactions. By running the\nunderlying driver in autocommit mode, we can eliminate all these checks, since\nwe are already managing transactions ourselves.\n\nBehaviorally, there should be no change -- Peewee will still treat top-level\nqueries outside of transactions as being autocommitted, while queries inside of\n`atomic()` / `with db:` blocks are implicitly committed at the end of the\nblock, or rolled-back if an exception occurs.\n\n**How might this affect me?**\n\n* If you are using the underlying database connection or cursors, e.g. via\n  `Database.connection()` or `Database.cursor()`, your queries will now be\n  executed in autocommit mode.\n* The `commit=` argument is deprecated for the `cursor()`, `execute()` and\n  `execute_sql()` methods.\n* If you have a custom `Database` implementation (whether for a database that\n  is not officially supported, or for the purpose of overriding default\n  behaviors), you will want to ensure that your connections are opened in\n  autocommit mode.\n\nOther changes:\n\n* Some fixes to help with packaging in Python 3.11.\n* MySQL `get_columns()` implementation now returns columns in their declared\n  order.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.15.4...3.16.0)\n\n## 3.15.4\n\n* Raise an exception in `ReconnectMixin` if connection is lost while inside a\n  transaction (if the transaction was interrupted presumably some changes were\n  lost and explicit intervention is needed).\n* Add `db.Model` property to reduce boilerplate.\n* Add support for running `prefetch()` queries with joins instead of subqueries\n  (this helps overcome a MySQL limitation about applying LIMITs to a subquery).\n* Add SQL `AVG` to whitelist to avoid coercing by default.\n* Allow arbitrary keywords in metaclass constructor, #2627\n* Add a `pyproject.toml` to silence warnings from newer pips when `wheel`\n  package is not available.\n\nThis release has a small helper for reducing boilerplate in some cases by\nexposing a base model class as an attribute of the database instance.\n\n```python\n# old:\ndb = SqliteDatabase('...')\n\nclass BaseModel(Model):\n    class Meta:\n        database = db\n\nclass MyModel(BaseModel):\n    pass\n\n# new:\ndb = SqliteDatabase('...')\n\nclass MyModel(db.Model):\n    pass\n```\n\n[View commits](https://github.com/coleifer/peewee/compare/3.15.3...3.15.4)\n\n## 3.15.3\n\n* Add `scalars()` query method (complements `scalar()`), roughly equivalent to\n  writing `[t[0] for t in query.tuples()]`.\n* Small doc improvements\n* Fix and remove some flaky test assertions with Sqlite INSERT + RETURNING.\n* Fix innocuous failing Sqlite test on big-endian machines.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.15.2...3.15.3)\n\n## 3.15.2\n\n* Fix bug where field-specific conversions were being applied to the pattern\n  used for LIKE / ILIKE operations. Refs #2609\n* Fix possible infinite loop when accidentally invoking the `__iter__` method\n  on certain `Column` subclasses. Refs #2606\n* Add new helper for specifying which Model a particular selected column-like\n  should be bound to, in queries with joins that select from multiple sources.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.15.1...3.15.2)\n\n## 3.15.1\n\n* Fix issue introduced in Sqlite 3.39.0 regarding the propagation of column\n  subtypes in subqueries.\n* Fix bug where cockroachdb server version was not set when beginning a\n  transaction on an unopened database.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.15.0...3.15.1)\n\n## 3.15.0\n\nRollback behavior change in commit ab43376697 (GH #2026). Peewee will no longer\nautomatically return the cursor `rowcount` for certain bulk-inserts.  This\nshould mainly affect users of MySQL and Sqlite who relied on a bulk INSERT\nreturning the `rowcount` (as opposed to the cursor's `lastrowid`). The\n`rowcount` behavior is still available chaining the ``as_rowcount()`` method:\n\n```python\n# NOTE: this change only affects MySQL or Sqlite.\ndb = MySQLDatabase(...)\n\n# Previously, bulk inserts of the following forms would return the rowcount.\nquery = User.insert_many(...)  # Bulk insert.\nquery = User.insert_from(...)  # Bulk insert (INSERT INTO .. SELECT FROM).\n\n# Previous behavior (peewee 3.12 - 3.14.10):\n# rows_inserted = query.execute()\n\n# New behavior:\nlast_id = query.execute()\n\n# To get the old behavior back:\nrows_inserted = query.as_rowcount().execute()\n```\n\nAdditionally, in previous versions specifying an empty `.returning()` with\nPostgres would cause the rowcount to be returned. For Postgres users who wish\nto receive the rowcount:\n\n```python\n# NOTE: this change only affects Postgresql.\ndb = PostgresqlDatabase(...)\n\n# Previously, an empty returning() would return the rowcount.\nquery = User.insert_many(...)  # Bulk insert.\nquery = User.insert_from(...)  # Bulk insert (INSERT INTO .. SELECT FROM).\n\n# Old behavior:\n# rows_inserted = query.returning().execute()\n\n# To get the rows inserted in 3.15 and newer:\nrows_inserted = query.as_rowcount().execute()\n```\n\nThis release contains a fix for a long-standing request to allow data-modifying\nqueries to support CTEs. CTEs are now supported for use with INSERT, DELETE and\nUPDATE queries - see #2152.\n\nAdditionally, this release adds better support for using the new `RETURNING`\nsyntax with Sqlite automatically. Specify `returning_clause=True` when\ninitializing your `SqliteDatabase` and all bulk inserts will automatically\nspecify a `RETURNING` clause, returning the newly-inserted primary keys. This\nfunctionality requires Sqlite 3.35 or newer.\n\nSmaller changes:\n\n* Add `shortcuts.insert_where()` helper for generating conditional INSERT with\n  a bit less boilerplate.\n* Fix bug in `test_utils.count_queres()` which could erroneously include pool\n  events such as connect/disconnect, etc.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.10...3.15.0)\n\n## 3.14.10\n\n* Add shortcut for conditional insert using sub-select, see #2528\n* Add convenience `left_outer_join()` method to query.\n* Add `selected_columns` property to Select queries.\n* Add `name` property to Alias instances.\n* Fix regression in tests introduced by change to DataSet in 3.14.9.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.9...3.14.10)\n\n## 3.14.9\n\n* Allow calling `table_exists()` with a model-class, refs\n* Improve `is_connection_usable()` method of `MySQLDatabase` class.\n* Better support for VIEWs with `playhouse.dataset.DataSet` and sqlite-web.\n* Support INSERT / ON CONFLICT in `playhosue.kv` for newer Sqlite.\n* Add `ArrayField.contained_by()` method, a corollary to `contains()` and\n  the `contains_any()` methods.\n* Support cyclical foreign-key relationships in reflection/introspection, and\n  also for sqlite-web.\n* Add magic methods for FTS5 field to optimize, rebuild and integrity check the\n  full-text index.\n* Add fallbacks in `setup.py` in the event distutils is not available.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.8...3.14.9)\n\n## 3.14.8\n\nBack-out all changes to automatically use RETURNING for `SqliteExtDatabase`,\n`CSqliteExtDatabase` and `APSWDatabase`. The issue I found is that when a\nRETURNING cursor is not fully-consumed, any parent SAVEPOINT (and possibly\ntransaction) would not be able to be released. Since this is a\nbackwards-incompatible change, I am going to back it out for now.\n\nReturning clause can still be specified for Sqlite, however it just needs to be\ndone so manually rather than having it applied automatically.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.7...3.14.8)\n\n## 3.14.7\n\nFix bug in APSW extension with Sqlite 3.35 and newer, due to handling of last\ninsert rowid with RETURNING. Refs #2479.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.6...3.14.7)\n\n## 3.14.6\n\nFix pesky bug in new `last_insert_id()` on the `SqliteExtDatabase`.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.5...3.14.6)\n\n## 3.14.5\n\nThis release contains a number of bug-fixes and small improvements.\n\n* Only raise `DoesNotExist` when `lazy_load` is enabled on ForeignKeyField,\n  fixes issue #2377.\n* Add missing convenience method `ModelSelect.get_or_none()`\n* Allow `ForeignKeyField` to specify a custom `BackrefAccessorClass`,\n  references issue #2391.\n* Ensure foreign-key-specific conversions are applied on INSERT and UPDATE,\n  fixes #2408.\n* Add handling of MySQL error 4031 (inactivity timeout) to the `ReconnectMixin`\n  helper class. Fixes #2419.\n* Support specification of conflict target for ON CONFLICT/DO NOTHING.\n* Add `encoding` parameter to the DataSet `freeze()` and `thaw()` methods,\n  fixes #2425.\n* Fix bug which prevented `DeferredForeignKey` from being used as a model's\n  primary key, fixes #2427.\n* Ensure foreign key's related object cache is cleared when the foreign-key is\n  set to `None`. Fixes #2428.\n* Allow specification of `(schema, table)` to be used with CREATE TABLE AS...,\n  fixes #2423.\n* Allow reusing open connections with DataSet, refs #2441.\n* Add `highlight()` and `snippet()` helpers to Sqlite `SearchField`, for use\n  with full-text search extension.\n* Preserve user-provided aliases in column names. Fixes #2453.\n* Add support for Sqlite 3.37 strict tables.\n* Ensure database is inherited when using `ThreadSafeDatabaseMetadata`, and\n  also adds an implementation in `playhouse.shortcuts` along with basic unit\n  tests.\n* Better handling of Model's dirty fields when saving, fixes #2466.\n* Add basic support for MariaDB connector driver in `playhouse.mysql_ext`, refs\n  issue #2471.\n* Begin a basic implementation for a psycopg3-compatible pg database, refs\n  issue #2473.\n* Add provisional support for RETURNING when using the appropriate versions of\n  Sqlite or MariaDB.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.4...3.14.5)\n\n## 3.14.4\n\nThis release contains an important fix for a regression introduced by commit\nebe3ad5, which affected the way model instances are converted to parameters for\nuse in expressions within a query. The bug could manifest when code uses model\ninstances as parameters in expressions against fields that are not\nforeign-keys.\n\nThe issue is described in #2376.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.3...3.14.4)\n\n## 3.14.3\n\nThis release contains a single fix for ensuring NULL values are inserted when\nissuing a bulk-insert of heterogeneous dictionaries which may be missing\nexplicit NULL values. Fixes issue #2638.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.2...3.14.3)\n\n## 3.14.2\n\nThis is a small release mainly to get some fixes out.\n\n* Support for named `Check` and foreign-key constraints.\n* Better foreign-key introspection for CockroachDB (and Postgres).\n* Register UUID adapter for Postgres.\n* Add `fn.array_agg()` to blacklist for automatic value coercion.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.1...3.14.2)\n\n## 3.14.1\n\nThis release contains primarily bugfixes.\n\n* Properly delegate to a foreign-key field's `db_value()` function when\n  converting model instances. #2304.\n* Strip quote marks and parentheses from column names returned by sqlite\n  cursor when a function-call is projected without an alias. #2305.\n* Fix `DataSet.create_index()` method, #2319.\n* Fix column-to-model mapping in model-select from subquery with joins, #2320.\n* Improvements to foreign-key lazy-loading thanks @conqp, #2328.\n* Preserve and handle `CHECK()` constraints in Sqlite migrator, #2343.\n* Add `stddev` aggregate function to collection of sqlite user-defined funcs.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.14.0...3.14.1)\n\n## 3.14.0\n\nThis release has been a bit overdue and there are numerous small improvements\nand bug-fixes. The bugfix that prompted this release is #2293, which is a\nregression in the Django-inspired `.filter()` APIs that could cause some\nfilter expressions to be discarded from the generated SQL. Many thanks for the\nexcellent bug report, Jakub.\n\n* Add an experimental helper, `shortcuts.resolve_multimodel_query()`, for\n  resolving multiple models used in a compound select query.\n* Add a `lateral()` method to select query for use with lateral joins, refs\n  issue #2205.\n* Added support for nested transactions (savepoints) in cockroach-db (requires\n  20.1 or newer).\n* Automatically escape wildcards passed to string-matching methods, refs #2224.\n* Allow index-type to be specified on MySQL, refs #2242.\n* Added a new API, `converter()` to be used for specifying a function to use to\n  convert a row-value pulled off the cursor, refs #2248.\n* Add `set()` and `clear()` method to the bitfield flag descriptor, refs #2257.\n* Add support for `range` types with `IN` and other expressions.\n* Support CTEs bound to compound select queries, refs #2289.\n\n### Bug-fixes\n\n* Fix to return related object id when accessing via the object-id descriptor,\n  when the related object is not populated, refs #2162.\n* Fix to ensure we do not insert a NULL value for a primary key.\n* Fix to conditionally set the field/column on an added column in a migration,\n  refs #2171.\n* Apply field conversion logic to model-class values. Relocates the logic from\n  issue #2131 and fixes #2185.\n* Clone node before modifying it to be flat in an enclosed nodelist expr, fixes\n  issue #2200.\n* Fix an invalid item assignment in nodelist, refs #2220.\n* Fix an incorrect truthiness check used with `save()` and `only=`, refs #2269.\n* Fix regression in `filter()` where using both `*args` and `**kwargs` caused\n  the expressions passed as `args` to be discarded. See #2293.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.13.3...3.14.0)\n\n## 3.13.3\n\n* Allow arbitrary keyword arguments to be passed to `DataSet` constructor,\n  which are then passed to the instrospector.\n* Allow scalar subqueries to be compared using numeric operands.\n* Fix `bulk_create()` when model being inserted uses FK identifiers.\n* Fix `bulk_update()` so that PK values are properly coerced to the right\n  data-type (e.g. UUIDs to strings for Sqlite).\n* Allow array indices to be used as dict keys, e.g. for the purposes of\n  updating a single array index value.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.13.2...3.13.3)\n\n## 3.13.2\n\n* Allow aggregate functions to support an `ORDER BY` clause, via the addition\n  of an `order_by()` method to the function (`fn`) instance. Refs #2094.\n* Fix `prefetch()` bug, where related \"backref\" instances were marked as dirty,\n  even though they had no changes. Fixes #2091.\n* Support `LIMIT 0`. Previously a limit of 0 would be translated into\n  effectively an unlimited query on MySQL. References #2084.\n* Support indexing into arrays using expressions with Postgres array fields.\n  References #2085.\n* Ensure postgres introspection methods return the columns for multi-column\n  indexes in the correct order. Fixes #2104.\n* Add support for arrays of UUIDs to postgres introspection.\n* Fix introspection of columns w/capitalized table names in postgres (#2110).\n* Fix to ensure correct exception is raised in SqliteQueueDatabase when\n  iterating over cursor/result-set.\n* Fix bug comparing subquery against a scalar value. Fixes #2118.\n* Fix issue resolving composite primary-keys that include foreign-keys when\n  building the model-graph. Fixes #2115.\n* Allow model-classes to be passed as arguments, e.g., to a table function.\n  Refs #2131.\n* Ensure postgres `JSONField.concat()` accepts expressions as arguments.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.13.1...3.13.2)\n\n## 3.13.1\n\nFix a regression when specifying keyword arguments to the `atomic()` or\n`transaction()` helper methods. Note: this only occurs if you were using Sqlite\nand were explicitly setting the `lock_type=` parameter.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.13.0...3.13.1)\n\n## 3.13.0\n\n### CockroachDB support added\n\nThis will be a notable release as it adds support for\n[CockroachDB](https://cockroachlabs.com/), a distributed, horizontally-scalable\nSQL database.\n\n* [CockroachDB usage overview](http://docs.peewee-orm.com/en/latest/peewee/database.html#using-crdb)\n* [CockroachDB API documentation](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#crdb)\n\n### Other features and fixes\n\n* Allow `FOR UPDATE` clause to specify one or more tables (`FOR UPDATE OF...`).\n* Support for Postgres `LATERAL` join.\n* Properly wrap exceptions raised during explicit commit/rollback in the\n  appropriate peewee-specific exception class.\n* Capture original exception object and expose it as `exc.orig` on the\n  wrapped exception.\n* Properly introspect `SMALLINT` columns in Postgres schema reflection.\n* More flexible handling of passing database-specific arguments to `atomic()`\n  and `transaction()` context-manager/decorator.\n* Fix non-deterministic join ordering issue when using the `filter()` API\n  across several tables (#2063).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.12.0...3.13.0)\n\n## 3.12.0\n\n* Bulk insert (`insert_many()` and `insert_from()`) will now return the row\n  count instead of the last insert ID. If you are using Postgres, peewee will\n  continue to return a cursor that provides an iterator over the newly-inserted\n  primary-key values by default. This behavior is being retained by default for\n  compatibility. Postgres users can simply specify an empty `returning()` call\n  to disable the cursor and retrieve the rowcount instead.\n* Migration extension now supports altering a column's data-type, via the new\n  `alter_column_type()` method.\n* Added `Database.is_connection_usabe()` method, which attempts to look at the\n  status of the underlying DB-API connection to determine whether the\n  connection is usable.\n* Common table expressions include a `materialized` parameter, which can be\n  used to control Postgres' optimization fencing around CTEs.\n* Added `BloomFilter.from_buffer()` method for populating a bloom-filter from\n  the output of a previous call to the `to_buffer()` method.\n* Fixed APSW extension's `commit()` and `rollback()` methods to no-op if the\n  database is in auto-commit mode.\n* Added `generate_always=` option to the `IdentityField` (defaults to False).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.11.2...3.12.0)\n\n## 3.11.2\n\n* Implement `hash` interface for `Alias` instances, allowing them to be used in\n  multi-source queries.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.11.1...3.11.2)\n\n## 3.11.1\n\n* Fix bug in new `_pk` / `get_id()` implementation for models that explicitly\n  have disabled a primary-key.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.11.0...3.11.1)\n\n## 3.11.0\n\n* Fixes #1991. This particular issue involves joining 3 models together in a\n  chain, where the outer two models are empty. Previously peewee would make the\n  middle model an empty model instance (since a link might be needed from the\n  source model to the outermost model). But since both were empty, it is more\n  correct to make the intervening model a NULL value on the foreign-key field\n  rather than an empty instance.\n* An unrelated fix came out of the work on #1991 where hashing a model whose\n  primary-key happened to be a foreign-key could trigger the FK resolution\n  query. This patch fixes the `Model._pk` and `get_id()` interfaces so they\n  no longer introduce the possibility of accidentally resolving the FK.\n* Allow `Field.contains()`, `startswith()` and `endswith()` to compare against\n  another column-like object or expression.\n* Workaround for MySQL prior to 8 and MariaDB handling of union queries inside\n  of parenthesized expressions (like IN).\n* Be more permissive in letting invalid values be stored in a field whose type\n  is INTEGER or REAL, since Sqlite allows this.\n* `TimestampField` resolution cleanup. Now values 0 *and* 1 will resolve to a\n  timestamp resolution of 1 second. Values 2-6 specify the number of decimal\n  places (hundredths to microsecond), or alternatively the resolution can still\n  be provided as a power of 10, e.g. 10, 1000 (millisecond), 1e6 (microsecond).\n* When self-referential foreign-keys are inherited, the foreign-key on the\n  subclass will also be self-referential (rather than pointing to the parent\n  model).\n* Add TSV import/export option to the `dataset` extension.\n* Add item interface to the `dataset.Table` class for doing primary-key lookup,\n  assignment, or deletion.\n* Extend the mysql `ReconnectMixin` helper to work with mysql-connector.\n* Fix mapping of double-precision float in postgres schema reflection.\n  Previously it mapped to single-precision, now it correctly uses a double.\n* Fix issue where `PostgresqlExtDatabase` and `MySQLConnectorDatabase` did not\n  respect the `autoconnect` setting.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.10.0...3.11.0)\n\n## 3.10.0\n\n* Add a helper to `playhouse.mysql_ext` for creating `Match` full-text search\n  expressions.\n* Added date-part properties to `TimestampField` for accessing the year, month,\n  day, etc., within a SQL expression.\n* Added `to_timestamp()` helper for `DateField` and `DateTimeField` that\n  produces an expression returning a unix timestamp.\n* Add `autoconnect` parameter to `Database` classes. This parameter defaults to\n  `True` and is compatible with previous versions of Peewee, in which executing\n  a query on a closed database would open a connection automatically. To make\n  it easier to catch inconsistent use of the database connection, this behavior\n  can now be disabled by specifying `autoconnect=False`, making an explicit\n  call to `Database.connect()` needed before executing a query.\n* Added database-agnostic interface for obtaining a random value.\n* Allow `isolation_level` to be specified when initializing a Postgres db.\n* Allow hybrid properties to be used on model aliases. Refs #1969.\n* Support aggregates with FILTER predicates on the latest Sqlite.\n\n#### Changes\n\n* More aggressively slot row values into the appropriate field when building\n  objects from the database cursor (rather than using whatever\n  `cursor.description` tells us, which is buggy in older Sqlite).\n* Be more permissive in what we accept in the `insert_many()` and `insert()`\n  methods.\n* When implicitly joining a model with multiple foreign-keys, choose the\n  foreign-key whose name matches that of the related model. Previously, this\n  would have raised a `ValueError` stating that multiple FKs existed.\n* Improved date truncation logic for Sqlite and MySQL to make more compatible\n  with Postgres' `date_trunc()` behavior. Previously, truncating a datetime to\n  month resolution would return `'2019-08'` for example. As of 3.10.0, the\n  Sqlite and MySQL `date_trunc` implementation returns a full datetime, e.g.\n  `'2019-08-01 00:00:00'`.\n* Apply slightly different logic for casting JSON values with Postgres.\n  Previously, Peewee just wrapped the value in the psycopg2 `Json()` helper.\n  In this version, Peewee now dumps the json to a string and applies an\n  explicit cast to the underlying JSON data-type (e.g. json or jsonb).\n\n#### Bug fixes\n\n* Save hooks can now be called for models without a primary key.\n* Fixed bug in the conversion of Python values to JSON when using Postgres.\n* Fix for differentiating empty values from NULL values in `model_to_dict`.\n* Fixed a bug referencing primary-key values that required some kind of\n  conversion (e.g., a UUID). See #1979 for details.\n* Add small jitter to the pool connection timestamp to avoid issues when\n  multiple connections are checked-out at the same exact time.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.9.6...3.10.0)\n\n## 3.9.6\n\n* Support nesting the `Database` instance as a context-manager. The outermost\n  block will handle opening and closing the connection along with wrapping\n  everything in a transaction. Nested blocks will use savepoints.\n* Add new `session_start()`, `session_commit()` and `session_rollback()`\n  interfaces to the Database object to support using transactional controls in\n  situations where a context-manager or decorator is awkward.\n* Fix error that would arise when attempting to do an empty bulk-insert.\n* Set `isolation_level=None` in SQLite connection constructor rather than\n  afterwards using the setter.\n* Add `create_table()` method to `Select` query to implement `CREATE TABLE AS`.\n* Cleanup some declarations in the Sqlite C extension.\n* Add new example showing how to implement Reddit's ranking algorithm in SQL.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.9.5...3.9.6)\n\n## 3.9.5\n\n* Added small helper for setting timezone when using Postgres.\n* Improved SQL generation for `VALUES` clause.\n* Support passing resolution to `TimestampField` as a power-of-10.\n* Small improvements to `INSERT` queries when the primary-key is not an\n  auto-incrementing integer, but is generated by the database server (eg uuid).\n* Cleanups to virtual table implementation and python-to-sqlite value\n  conversions.\n* Fixed bug related to binding previously-unbound models to a database using a\n  context manager, #1913.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.9.4...3.9.5)\n\n## 3.9.4\n\n* Add `Model.bulk_update()` method for bulk-updating fields across multiple\n  model instances. [Docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.bulk_update).\n* Add `lazy_load` parameter to `ForeignKeyField`. When initialized with\n  `lazy_load=False`, the foreign-key will not use an additional query to\n  resolve the related model instance. Instead, if the related model instance is\n  not available, the underlying FK column value is returned (behaving like the\n  \"_id\" descriptor).\n* Added `Model.truncate_table()` method.\n* The `reflection` and `pwiz` extensions now attempt to be smarter about\n  converting database table and column names into snake-case. To disable this,\n  you can set `snake_case=False` when calling the `Introspector.introspect()`\n  method or use the `-L` (legacy naming) option with the `pwiz` script.\n* Bulk insert via ``insert_many()`` no longer require specification of the\n  fields argument when the inserted rows are lists/tuples. In that case, the\n  fields will be inferred to be all model fields except any auto-increment id.\n* Add `DatabaseProxy`, which implements several of the `Database` class context\n  managers. This allows you to reference some of the special features of the\n  database object without directly needing to initialize the proxy first.\n* Add support for window function frame exclusion and added built-in support\n  for the GROUPS frame type.\n* Add support for chaining window functions by extending a previously-declared\n  window function.\n* Playhouse Postgresql extension `TSVectorField.match()` method supports an\n  additional argument `plain`, which can be used to control the parsing of the\n  TS query.\n* Added very minimal `JSONField` to the playhouse MySQL extension.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.9.3...3.9.4)\n\n## 3.9.3\n\n* Added cross-database support for `NULLS FIRST/LAST` when specifying the\n  ordering for a query. Previously this was only supported for Postgres. Peewee\n  will now generate an equivalent `CASE` statement for Sqlite and MySQL.\n* Added [EXCLUDED](http://docs.peewee-orm.com/en/latest/peewee/api.html#EXCLUDED)\n  helper for referring to the `EXCLUDED` namespace used with `INSERT...ON CONFLICT`\n  queries, when referencing values in the conflicting row data.\n* Added helper method to the model `Metadata` class for setting the table name\n  at run-time. Setting the `Model._meta.table_name` directly may have appeared\n  to work in some situations, but could lead to subtle bugs. The new API is\n  `Model._meta.set_table_name()`.\n* Enhanced helpers for working with Peewee interactively, [see doc](http://docs.peewee-orm.com/en/latest/peewee/interactive.html).\n* Fix cache invalidation bug in `DataSet` that was originally reported on the\n  sqlite-web project.\n* New example script implementing a [hexastore](https://github.com/coleifer/peewee/blob/master/examples/hexastore.py).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.9.2...3.9.3)\n\n## 3.9.1 and 3.9.2\n\nIncludes a bugfix for an `AttributeError` that occurs when using MySQL with the\n`MySQLdb` client. The 3.9.2 release includes fixes for a test failure.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.9.0...3.9.2)\n\n## 3.9.0\n\n* Added new document describing how to [use peewee interactively](http://docs.peewee-orm.com/en/latest/peewee/interactive.html).\n* Added convenience functions for generating model classes from a pre-existing\n  database, printing model definitions and printing CREATE TABLE sql for a\n  model. See the \"use peewee interactively\" section for details.\n* Added a `__str__` implementation to all `Query` subclasses which converts the\n  query to a string and interpolates the parameters.\n* Improvements to `sqlite_ext.JSONField` regarding the serialization of data,\n  as well as the addition of options to override the JSON serialization and\n  de-serialization functions.\n* Added `index_type` parameter to `Field`\n* Added `DatabaseProxy`, which allows one to use database-specific decorators\n  with an uninitialized `Proxy` object. See #1842 for discussion. Recommend\n  that you update any usage of `Proxy` for deferring database initialization to\n  use the new `DatabaseProxy` class instead.\n* Added support for `INSERT ... ON CONFLICT` when the conflict target is a\n  partial index (e.g., contains a `WHERE` clause). The `OnConflict` and\n  `on_conflict()` APIs now take an additional `conflict_where` parameter to\n  represent the `WHERE` clause of the partial index in question. See #1860.\n* Enhanced the `playhouse.kv` extension to use efficient upsert for *all*\n  database engines. Previously upsert was only supported for sqlite and mysql.\n* Re-added the `orwhere()` query filtering method, which will append the given\n  expressions using `OR` instead of `AND`. See #391 for old discussion.\n* Added some new examples to the ``examples/`` directory\n* Added `select_from()` API for wrapping a query and selecting one or more\n  columns from the wrapped subquery. [Docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#SelectQuery.select_from).\n* Added documentation on using [row values](http://docs.peewee-orm.com/en/latest/peewee/query_operators.html#row-values).\n* Removed the (defunct) \"speedups\" C extension, which as of 3.8.2 only\n  contained a barely-faster function for quoting entities.\n\n**Bugfixes**\n\n* Fix bug in SQL generation when there was a subquery that used a common table\n  expressions.\n* Enhanced `prefetch()` and fixed bug that could occur when mixing\n  self-referential foreign-keys and model aliases.\n* MariaDB 10.3.3 introduces backwards-incompatible changes to the SQL used for\n  upsert. Peewee now introspects the MySQL server version at connection time to\n  ensure proper handling of version-specific features. See #1834 for details.\n* Fixed bug where `TimestampField` would treat zero values as `None` when\n  reading from the database.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.8.2...3.9.0)\n\n## 3.8.2\n\n**Backwards-incompatible changes**\n\n* The default row-type for `INSERT` queries executed with a non-default\n  `RETURNING` clause has changed from `tuple` to `Model` instances. This makes\n  `INSERT` behavior consistent with `UPDATE` and `DELETE` queries that specify\n  a `RETURNING` clause. To revert back to the old behavior, just append a call\n  to `.tuples()` to your `INSERT ... RETURNING` query.\n* Removing support for the `table_alias` model `Meta` option. Previously, this\n  attribute could be used to specify a \"vanity\" alias for a model class in the\n  generated SQL. As a result of some changes to support more robust UPDATE and\n  DELETE queries, supporting this feature will require some re-working. As of\n  the 3.8.0 release, it was broken and resulted in incorrect SQL for UPDATE\n  queries, so now it is removed.\n\n**New features**\n\n* Added `playhouse.shortcuts.ReconnectMixin`, which can be used to implement\n  automatic reconnect under certain error conditions (notably the MySQL error\n  2006 - server has gone away).\n\n**Bugfixes**\n\n* Fix SQL generation bug when using an inline window function in the `ORDER BY`\n  clause of a query.\n* Fix possible zero-division in user-defined implementation of BM25 ranking\n  algorithm for SQLite full-text search.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.8.1...3.8.2)\n\n## 3.8.1\n\n**New features**\n\n* Sqlite `SearchField` now supports the `match()` operator, allowing full-text\n  search to be performed on a single column (as opposed to the whole table).\n\n**Changes**\n\n* Remove minimum passphrase restrictions in SQLCipher integration.\n\n**Bugfixes**\n\n* Support inheritance of `ManyToManyField` instances.\n* Ensure operator overloads are invoked when generating filter expressions.\n* Fix incorrect scoring in Sqlite BM25, BM25f and Lucene ranking algorithms.\n* Support string field-names in data dictionary when performing an ON CONFLICT\n  ... UPDATE query, which allows field-specific conversions to be applied.\n  References #1815.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.8.0...3.8.1)\n\n## 3.8.0\n\n**New features**\n\n* Postgres `BinaryJSONField` now supports `has_key()`, `concat()` and\n  `remove()` methods (though remove may require pg10+).\n* Add `python_value()` method to the SQL-function helper `fn`, to allow\n  specifying a custom function for mapping database values to Python values.\n\n**Changes**\n\n* Better support for UPDATE ... FROM queries, and more generally, more robust\n  support for UPDATE and RETURNING clauses. This means that the\n  `QualifiedNames` helper is no longer needed for certain types of queries.\n* The `SqlCipherDatabase` no longer accepts a `kdf_iter` parameter. To\n  configure the various SQLCipher encryption settings, specify the setting\n  values as `pragmas` when initializing the database.\n* Introspection will now, by default, only strip \"_id\" from introspected column\n  names if those columns are foreign-keys. See #1799 for discussion.\n* Allow `UUIDField` and `BinaryUUIDField` to accept hexadecimal UUID strings as\n  well as raw binary UUID bytestrings (in addition to `UUID` instances, which\n  are already supported).\n* Allow `ForeignKeyField` to be created without an index.\n* Allow multiple calls to `cast()` to be chained (#1795).\n* Add logic to ensure foreign-key constraint names that exceed 64 characters\n  are truncated using the same logic as is currently in place for long indexes.\n* `ManyToManyField` supports foreign-keys to fields other than primary-keys.\n* When linked against SQLite 3.26 or newer, support `SQLITE_CONSTRAINT` to\n  designate invalid queries against virtual tables.\n* SQL-generation changes to aid in supporting using queries within expressions\n  following the SELECT statement.\n\n**Bugfixes**\n\n* Fixed bug in `order_by_extend()`, thanks @nhatHero.\n* Fixed bug where the `DataSet` CSV import/export did not support non-ASCII\n  characters in Python 3.x.\n* Fixed bug where `model_to_dict` would attempt to traverse explicitly disabled\n  foreign-key backrefs (#1785).\n* Fixed bug when attempting to migrate SQLite tables that have a field whose\n  column-name begins with \"primary_\".\n* Fixed bug with inheriting deferred foreign-keys.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.7.1...3.8.0)\n\n## 3.7.1\n\n**New features**\n\n* Added `table_settings` model `Meta` option, which should be a list of strings\n  specifying additional options for `CREATE TABLE`, which are placed *after*\n  the closing parentheses.\n* Allow specification of `on_update` and `on_delete` behavior for many-to-many\n  relationships when using `ManyToManyField`.\n\n**Bugfixes**\n\n* Fixed incorrect SQL generation for Postgresql ON CONFLICT clause when the\n  conflict_target is a named constraint (rather than an index expression). This\n  introduces a new keyword-argument to the `on_conflict()` method:\n  `conflict_constraint`, which is currently only supported by Postgresql. Refs\n  issue #1737.\n* Fixed incorrect SQL for sub-selects used on the right side of `IN`\n  expressions. Previously the query would be assigned an alias, even though an\n  alias was not needed.\n* Fixed incorrect SQL generation for Model indexes which contain SQL functions\n  as indexed columns.\n* Fixed bug in the generation of special queries used to perform operations on\n  SQLite FTS5 virtual tables.\n* Allow `frozenset` to be correctly parameterized as a list of values.\n* Allow multi-value INSERT queries to specify `columns` as a list of strings.\n* Support `CROSS JOIN` for model select queries.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.7.0...3.7.1)\n\n## 3.7.0\n\n**Backwards-incompatible changes**\n\n* Pool database `close_all()` method renamed to `close_idle()` to better\n  reflect the actual behavior.\n* Databases will now raise `InterfaceError` when `connect()` or `close()` are\n  called on an uninitialized, deferred database object.\n\n**New features**\n\n* Add methods to the migrations extension to support adding and dropping table\n  constraints.\n* Add [Model.bulk_create()](http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.bulk_create)\n  method for bulk-inserting unsaved model instances.\n* Add `close_stale()` method to the connection pool to support closing stale\n  connections.\n* The `FlaskDB` class in `playhouse.flask_utils` now accepts a `model_class`\n  parameter, which can be used to specify a custom base-class for models.\n\n**Bugfixes**\n\n* Parentheses were not added to subqueries used in function calls with more\n  than one argument.\n* Fixed bug when attempting to serialize many-to-many fields which were created\n  initially with a `DeferredThroughModel`, see #1708.\n* Fixed bug when using the Postgres `ArrayField` with an array of `BlobField`.\n* Allow `Proxy` databases to be used as a context-manager.\n* Fixed bug where the APSW driver was referring to the SQLite version from the\n  standard library `sqlite3` driver, rather than from `apsw`.\n* Reflection library attempts to wrap server-side column defaults in quotation\n  marks if the column data-type is text/varchar.\n* Missing import in migrations library, which would cause errors when\n  attempting to add indexes whose name exceeded 64 chars.\n* When using the Postgres connection pool, ensure any open/pending transactions\n  are rolled-back when the connection is recycled.\n* Even *more* changes to the `setup.py` script. In this case I've added a\n  helper function which will reliably determine if the SQLite3 extensions can\n  be built. This follows the approach taken by the Python YAML package.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.6.4...3.7.0)\n\n## 3.6.4\n\nTake a whole new approach, following what `simplejson` does. Allow the\n`build_ext` command class to fail, and retry without extensions in the event we\nrun into issues building extensions. References #1676.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.6.3...3.6.4)\n\n## 3.6.3\n\nAdd check in `setup.py` to determine if a C compiler is available before\nbuilding C extensions. References #1676.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.6.2...3.6.3)\n\n## 3.6.2\n\nUse `ctypes.util.find_library` to determine if `libsqlite3` is installed.\nShould fix problems people are encountering installing when SQLite3 is not\navailable.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.6.1...3.6.2)\n\n## 3.6.1\n\nFixed issue with setup script.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.6.0...3.6.1)\n\n## 3.6.0\n\n* Support for Python 3.7, including bugfixes related to new StopIteration\n  handling inside of generators.\n* Support for specifying `ROWS` or `RANGE` window frame types. For more\n  information, see the new [frame type documentation](http://docs.peewee-orm.com/en/latest/peewee/querying.html#frame-types-range-vs-rows).\n* Add APIs for user-defined window functions if using [pysqlite3](https://github.com/coleifer/pysqlite3)\n  and sqlite 3.25.0 or newer.\n* `TimestampField` now uses 64-bit integer data-type for storage.\n* Added support to `pwiz` and `playhouse.reflection` to enable generating\n  models from VIEWs.\n* Added lower-level database API for introspecting VIEWs.\n* Revamped continuous integration setup for better coverage, including 3.7 and\n  3.8-dev.\n* Allow building C extensions even if Cython is not installed, by distributing\n  pre-generated C source files.\n* Switch to using `setuptools` for packaging.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.5.2...3.6.0)\n\n## 3.5.2\n\n* New guide to using [window functions in Peewee](http://docs.peewee-orm.com/en/latest/peewee/querying.html#window-functions).\n* New and improved table name auto-generation. This feature is not backwards\n  compatible, so it is **disabled by default**. To enable, set\n  `legacy_table_names=False` in your model's `Meta` options. For more details,\n  see [table names](http://docs.peewee-orm.com/en/latest/peewee/models.html#table_names)\n  documentation.\n* Allow passing single fields/columns to window function `order_by` and\n  `partition_by` arguments.\n* Support for `FILTER (WHERE...)` clauses with window functions and aggregates.\n* Added `IdentityField` class suitable for use with Postgres 10's new identity\n  column type. It can be used anywhere `AutoField` or `BigAutoField` was being\n  used previously.\n* Fixed bug creating indexes on tables that are in attached databases (SQLite).\n* Fixed obscure bug when using `prefetch()` and `ModelAlias` to populate a\n  back-reference related model.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.5.1...3.5.2)\n\n## 3.5.1\n\n**New features**\n\n* New documentation for working with [relationships](http://docs.peewee-orm.com/en/latest/peewee/relationships.html)\n  in Peewee.\n* Improved tests and documentation for MySQL upsert functionality.\n* Allow `database` parameter to be specified with `ModelSelect.get()` method.\n  For discussion, see #1620.\n* Add `QualifiedNames` helper to peewee module exports.\n* Add `temporary=` meta option to support temporary tables.\n* Allow a `Database` object to be passed to constructor of `DataSet` helper.\n\n**Bug fixes**\n\n* Fixed edge-case where attempting to alias a field to it's underlying\n  column-name (when different), Peewee would not respect the alias and use the\n  field name instead. See #1625 for details and discussion.\n* Raise a `ValueError` when joining and aliasing the join to a foreign-key's\n  `object_id_name` descriptor. Should prevent accidentally introducing O(n)\n  queries or silently ignoring data from a joined-instance.\n* Fixed bug for MySQL when creating a foreign-key to a model which used the\n  `BigAutoField` for it's primary-key.\n* Fixed bugs in the implementation of user-defined aggregates and extensions\n  with the APSW SQLite driver.\n* Fixed regression introduced in 3.5.0 which ignored custom Model `__repr__()`.\n* Fixed regression from 2.x in which inserting from a query using a `SQL()` was\n  no longer working. Refs #1645.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.5.0...3.5.1)\n\n## 3.5.0\n\n**Backwards-incompatible changes**\n\n* Custom Model `repr` no longer use the convention of overriding `__unicode__`,\n  and now use `__str__`.\n* Redesigned the [sqlite json1 integration](http://docs.peewee-orm.com/en/latest/peewee/sqlite_ext.html#sqlite-json1).\n  and changed some of the APIs and semantics of various `JSONField` methods.\n  The documentation has been expanded to include more examples and the API has\n  been simplified to make it easier to work with. These changes **do not** have\n  any effect on the [Postgresql JSON fields](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#pgjson).\n\n**New features**\n\n* Better default `repr` for model classes and fields.\n* `ForeignKeyField()` accepts a new initialization parameter, `deferrable`, for\n  specifying when constraints should be enforced.\n* `BitField.flag()` can be called without a value parameter for the common\n  use-case of using flags that are powers-of-2.\n* `SqliteDatabase` pragmas can be specified as a `dict` (previously required a\n  list of 2-tuples).\n* SQLite `TableFunction` ([docs](http://docs.peewee-orm.com/en/latest/peewee/sqlite_ext.html#sqlite-vtfunc))\n  will print Python exception tracebacks raised in the `initialize` and\n  `iterate` callbacks, making debugging significantly easier.\n\n**Bug fixes**\n\n* Fixed bug in `migrator.add_column()` where, if the field being added declared\n  a non-standard index type (e.g., binary json field with GIN index), this\n  index type was not being respected.\n* Fixed bug in `database.table_exists()` where the implementation did not match\n  the documentation. Implementation has been updated to match the\n  documentation.\n* Fixed bug in SQLite `TableFunction` implementation which raised errors if the\n  return value of the `iterate()` method was not a `tuple`.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.4.0...3.5.0)\n\n## 3.4.0\n\n**Backwards-incompatible changes**\n\n* The `regexp()` operation is now case-sensitive for MySQL and Postgres. To\n  perform case-insensitive regexp operations, use `iregexp()`.\n* The SQLite `BareField()` field-type now supports all column constraints\n  *except* specifying the data-type. Previously it silently ignored any column\n  constraints.\n* LIMIT and OFFSET parameters are now treated as parameterized values instead\n  of literals.\n* The `schema` parameter for SQLite database introspection methods is no longer\n  ignored by default. The schema corresponds to the name given to an attached\n  database.\n* `ArrayField` now accepts a new parameter `field_kwargs`, which is used to\n  pass information to the array field's `field_class` initializer.\n\n**New features and other changes**\n\n* SQLite backup interface supports specifying page-counts and a user-defined\n  progress handler.\n* GIL is released when doing backups or during SQLite busy timeouts (when using\n  the peewee SQLite busy-handler).\n* Add NATURAL join-type to the `JOIN` helper.\n* Improved identifier quoting to allow specifying distinct open/close-quote\n  characters. Enables adding support for MSSQL, for instance, which uses square\n  brackets, e.g. `[table].[column]`.\n* Unify timeout interfaces for SQLite databases (use seconds everywhere rather\n  than mixing seconds and milliseconds, which was confusing).\n* Added `attach()` and `detach()` methods to SQLite database, making it\n  possible to attach additional databases (e.g. an in-memory cache db).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.3.4...3.4.0)\n\n## 3.3.4\n\n* Added a `BinaryUUIDField` class for efficiently storing UUIDs in 16-bytes.\n* Fix dataset's `update_cache()` logic so that when updating a single table\n  that was newly-added, we also ensure that all dependent tables are updated at\n  the same time. Refs coleifer/sqlite-web#42.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.3.3...3.3.4)\n\n## 3.3.3\n\n* More efficient implementation of model dependency-graph generation. Improves\n  performance of recursively deleting related objects by omitting unnecessary\n  subqueries.\n* Added `union()`, `union_all()`, `intersect()` and `except_()` to the\n  `Model`-specific query implementations. This was an oversight that should\n  have been patched in 3.3.2, but is fixed in 3.3.3.\n* Major cleanup to test runner and standardized test skipping logic to\n  integrate with standard-library `unittest` conventions.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.3.2...3.3.3)\n\n## 3.3.2\n\n* Add methods for `union()`, `union_all`, `intersect()` and `except_()`.\n  Previously, these methods were only available as operator overloads.\n* Removed some Python 2.6-specific support code, as 2.6 is no longer officially\n  supported.\n* Fixed model-graph resolution logic for deferred foreign-keys.\n* Better support for UPDATE...FROM queries (Postgresql).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.3.1...3.3.2)\n\n## 3.3.1\n\n* Fixed long-standing bug in 3.x regarding using column aliases with queries\n  that utilize the ModelCursorWrapper (typically queries with one or more\n  joins).\n* Fix typo in model metadata code, thanks @klen.\n* Add examples of using recursive CTEs to docs.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.3.0...3.3.1)\n\n## 3.3.0\n\n* Added support for SQLite's new `ON CONFLICT` clause, which is modelled on the\n  syntax used by Postgresql and will be available in SQLite 3.24.0 and onward.\n* Added better support for using common table expressions and a cleaner way of\n  implementing recursive CTEs, both of which are also tested with integration\n  tests (as opposed to just checking the generated SQL).\n* Modernized the CI environment to utilize the latest MariaDB features, so we\n  can test window functions and CTEs with MySQL (when available).\n* Reorganized and unified the feature-flags in the test suite.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.2.5...3.3.0)\n\n## 3.2.5\n\n* Added `ValuesList` for representing values lists. [Docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#ValuesList).\n* `DateTimeField`, `DateField` and `TimeField` will parse formatted-strings\n  before sending to the database. Previously this only occurred when reading\n  values from the database.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.2.4...3.2.5)\n\n## 3.2.4\n\n* Smarter handling of model-graph when dealing with compound queries (union,\n  intersect, etc). #1579.\n* If the same column-name is selected multiple times, first value wins. #1579.\n* If `ModelSelect.switch()` is called without any arguments, default to the\n  query's model. Refs #1573.\n* Fix issue where cloning a ModelSelect query did not result in the joins being\n  cloned. #1576.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.2.3...3.2.4)\n\n## 3.2.3\n\n* `pwiz` tool will capture column defaults defined as part of the table schema.\n* Fixed a misleading error message - #1563.\n* Ensure `reuse_if_open` parameter has effect on pooled databases.\n* Added support for on update/delete when migrating foreign-key.\n* Fixed bug in SQL generation for subqueries in aliased functions #1572.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.2.2...3.2.3)\n\n## 3.2.2\n\n* Added support for passing `Model` classes to the `returning()` method when\n  you intend to return all columns for the given model.\n* Fixed a bug when using user-defined sequences, and the underlying sequence\n  already exists.\n* Added `drop_sequences` parameter to `drop_table()` method which allows you to\n  conditionally drop any user-defined sequences when dropping the table.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.2.1...3.2.2)\n\n## 3.2.1\n\n**Notice:** the default mysql driver for Peewee has changed to [pymysql](https://github.com/PyMySQL/PyMySQL)\nin version 3.2.1. In previous versions, if both *mysql-python* and *pymysql*\nwere installed, Peewee would use *mysql-python*. As of 3.2.1, if both libraries\nare installed Peewee will use *pymysql*.\n\n* Added new module `playhouse.mysql_ext` which includes\n  `MySQLConnectorDatabase`, a database implementation that works with the\n  [mysql-connector](https://dev.mysql.com/doc/connector-python/en/) driver.\n* Added new field to `ColumnMetadata` class which captures a database column's\n  default value. `ColumnMetadata` is returned by `Database.get_columns()`.\n* Added [documentation on making Peewee async](http://docs.peewee-orm.com/en/latest/peewee/database.html#async-with-gevent).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.2.0...3.2.1)\n\n## 3.2.0\n\nThe 3.2.0 release introduces a potentially backwards-incompatible change. The\nonly users affected will be those that have implemented custom `Field` types\nwith a user-defined `coerce` method. tl/dr: rename the coerce attribute to\nadapt and you should be set.\n\n#### Field.coerce renamed to Field.adapt\n\nThe `Field.coerce` method has been renamed to `Field.adapt`. The purpose of\nthis method is to convert a value from the application/database into the\nappropriate Python data-type. For instance, `IntegerField.adapt` is simply the\n`int` built-in function.\n\nThe motivation for this change is to support adding metadata to any AST node\ninstructing Peewee to not coerce the associated value. As an example, consider\nthis code:\n\n```python\n\nclass Note(Model):\n    id = AutoField()  # autoincrementing integer primary key.\n    content = TextField()\n\n# Query notes table and cast the \"id\" to a string and store as \"id_text\" attr.\nquery = Note.select(Note.id.cast('TEXT').alias('id_text'), Note.content)\n\na_note = query.get()\nprint((a_note.id_text, a_note.content))\n\n# Prior to 3.2.0 the CAST is \"un-done\" because the value gets converted\n# back to an integer, since the value is associated with the Note.id field:\n(1, u'some note')  # 3.1.7, e.g. -- \"id_text\" is an integer!\n\n# As of 3.2.0, CAST will automatically prevent the conversion of field values,\n# which is an extension of a more general metadata API that can instruct Peewee\n# not to convert certain values.\n(u'1', u'some note')  # 3.2.0 -- \"id_text\" is a string as expected.\n```\n\nIf you have implemented custom `Field` classes and are using `coerce` to\nenforce a particular data-type, you can simply rename the attribute to `adapt`.\n\n#### Other changes\n\nOld versions of SQLite do not strip quotation marks from aliased column names\nin compound queries (e.g. UNION). Fixed in 3.2.0.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.7...3.2.0)\n\n## 3.1.7\n\nFor all the winblows lusers out there, added an option to skip compilation of\nthe SQLite C extensions during installation. Set env var `NO_SQLITE=1` and run\n`setup.py install` and you should be able to build without requiring SQLite.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.6...3.1.7)\n\n## 3.1.6\n\n* Added `rekey()` method to SqlCipher database for changing encryption key and\n  documentation for `set_passphrase()` method.\n* Added `convert_values` parameter to `ArrayField` constructor, which will\n  cause the array values to be processed using the underlying data-type's\n  conversion logic.\n* Fixed unreported bug using `TimestampField` with sub-second resolutions.\n* Fixed bug where options were not being processed when calling `drop_table()`.\n* Some fixes and improvements to `signals` extension.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.5...3.1.6)\n\n## 3.1.5\n\nFixed Python 2/3 incompatibility with `itertools.izip_longest()`.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.4...3.1.5)\n\n## 3.1.4\n\n* Added `BigAutoField` to support 64-bit auto-incrementing primary keys.\n* Use Peewee-compatible datetime serialization when exporting JSON from\n  a `DataSet`. Previously the JSON export used ISO-8601 by default. See #1536.\n* Added `Database.batch_commit` helper to wrap iterators in chunked\n  transactions. See #1539 for discussion.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.3...3.1.4)\n\n## 3.1.3\n\n* Fixed issue where scope-specific settings were being updated in-place instead\n  of copied. #1534.\n* Fixed bug where setting a `ForeignKeyField` did not add it to the model's\n  \"dirty\" fields list. #1530.\n* Use pre-fetched data when using `prefetch()` with `ManyToManyField`. Thanks\n  to @iBelieve for the patch. #1531.\n* Use `JSON` data-type for SQLite `JSONField` instances.\n* Add a `json_contains` function for use with SQLite `json1` extension.\n* Various documentation updates and additions.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.2...3.1.3)\n\n## 3.1.2\n\n#### New behavior for INSERT queries with RETURNING clause\n\nInvestigating #1522, it occurred to me that INSERT queries with non-default\n*RETURNING* clauses (postgres-only feature) should always return a cursor\nobject. Previously, if executing a single-row INSERT query, the last-inserted\nrow ID would be returned, regardless of what was specified by the RETURNING\nclause.\n\nThis change only affects INSERT queries with non-default RETURNING clauses and\nwill cause a cursor to be returned, as opposed to the last-inserted row ID.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.1...3.1.2)\n\n## 3.1.1\n\n* Fixed bug when using `Model.alias()` when the model defined a particular\n  database schema.\n* Added `SchemaManager.create_foreign_key` API to simplify adding constraints\n  when dealing with circular foreign-key relationships. Updated docs\n  accordingly.\n* Improved implementation of `Migrator.add_foreign_key_constraint` so that it\n  can be used with Postgresql (in addition to MySQL).\n* Added `PickleField` to the `playhouse.fields` module. [Docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#PickleField).\n* Fixed bug in implementation of `CompressedField` when using Python 3.\n* Added `KeyValue` API in `playhouse.kv` module. [Docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#key-value-store).\n* More test cases for joining on sub-selects or common table expressions.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.1.0...3.1.1)\n\n## 3.1.0\n\n#### Backwards-incompatible changes\n\n`Database.bind()` has been renamed to `Database.bind_ctx()`, to more closely\nmatch the semantics of the corresponding model methods, `Model.bind()` and\n`Model.bind_ctx()`. The new `Database.bind()` method is a one-time operation\nthat binds the given models to the database. See documentation:\n\n* [Database.bind()](http://docs.peewee-orm.com/en/latest/peewee/api.html#Database.bind)\n* [Database.bind_ctx()](http://docs.peewee-orm.com/en/latest/peewee/api.html#Database.bind_ctx)\n\n#### Other changes\n\n* Removed Python 2.6 support code from a few places.\n* Fixed example analytics app code to ensure hstore extension is registered.\n* Small efficiency improvement to bloom filter.\n* Removed \"attention!\" from *README*.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.20...3.1.0)\n\n## 3.0.20\n\n* Include `schema` (if specified) when checking for table-existence.\n* Correct placement of ORDER BY / LIMIT clauses in compound select queries.\n* Fix bug in back-reference lookups when using `filter()` API.\n* Fix bug in SQL generation for ON CONFLICT queries with Postgres, #1512.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.19...3.0.20)\n\n## 3.0.19\n\n* Support for more types of mappings in `insert_many()`, refs #1495.\n* Lots of documentation improvements.\n* Fix bug when calling `tuples()` on a `ModelRaw` query. This was reported\n  originally as a bug with *sqlite-web* CSV export. See coleifer/sqlite-web#38.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.18...3.0.19)\n\n## 3.0.18\n\n* Improved error messages when attempting to use a database class for which the\n  corresponding driver is not installed.\n* Added tests showing the use of custom operator (a-la the docs).\n* Fixed indentation issue in docs, #1493.\n* Fixed issue with the SQLite date_part issue, #1494.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.17...3.0.18)\n\n## 3.0.17\n\n* Fix `schema` inheritance regression, #1485.\n* Add helper method to postgres migrator for setting search_path, #1353.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.16...3.0.17)\n\n## 3.0.16\n\n* Improve model graph resolution when iterating results of a query. Refs #1482.\n* Allow Model._meta.schema to be changed at run-time. #1483.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.15...3.0.16)\n\n## 3.0.15\n\n* Use same `schema` used for reflection in generated models.\n* Preserve `pragmas` set on deferred Sqlite database if database is\n  re-initialized without re-specifying pragmas.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.14...3.0.15)\n\n## 3.0.14\n\n* Fix bug creating model instances on Postgres when model does not have a\n  primary key column.\n* Extend postgresql reflection to support array types.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.13...3.0.14)\n\n## 3.0.13\n\n* Fix bug where simple field aliases were being ignored. Fixes #1473.\n* More strict about column type inference for postgres + pwiz.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.12...3.0.13)\n\n## 3.0.12\n\n* Fix queries of the form INSERT ... VALUES (SELECT...) so that sub-select is\n  wrapped in parentheses.\n* Improve model-graph resolution when selecting from multiple tables that are\n  joined by foreign-keys, and an intermediate table is omitted from selection.\n* Docs update to reflect deletion of post_init signal.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.11...3.0.12)\n\n## 3.0.11\n\n* Add note to changelog about `cursor()` method.\n* Add hash method to postgres indexedfield subclasses.\n* Add TableFunction to sqlite_ext module namespace.\n* Fix bug regarding NOT IN queries where the right-hand-side is an empty set.\n* Fallback implementations of bm25f and lucene search ranking algorithms.\n* Fixed DecimalField issue.\n* Fixed issue with BlobField when database is a Proxy object.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.10...3.0.11)\n\n## 3.0.10\n\n* Fix `Database.drop_tables()` signature to support `cascade` argument - #1453.\n* Fix querying documentation for custom functions - #1454.\n* Added len() method to `ModelBase` for convenient counting.\n* Fix bug related to unsaved relation population (thanks @conqp) - #1459.\n* Fix count() on compound select - #1460.\n* Support `coerce` keyword argument with `fn.XXX()` - #1463.\n* Support updating existing model instance with dict_to_model-like API - #1456.\n* Fix equality tests with ArrayField - #1461.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.9...3.0.10)\n\n## 3.0.9\n\n* Add deprecation notice if passing `autocommit` as keyword argument to the\n  `Database` initializer. Refs #1452.\n* Add `JSONPath` and \"J\" helpers to sqlite extension.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.8...3.0.9)\n\n## 3.0.8\n\n* Add support for passing `cascade=True` when dropping tables. Fixes #1449.\n* Fix issues with backrefs and inherited foreign-keys. Fixes #1448.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.7...3.0.8)\n\n## 3.0.7\n\n* Add `select_extend()` method to extend existing SELECT-ion. [Doc](http://docs.peewee-orm.com/en/latest/peewee/api.html#Select.select_extend).\n* Accept `set()` as iterable value type, fixes #1445\n* Add test for model/field inheritance and fix bug relating to recursion error\n  when inheriting foreign-key field. Fixes #1448.\n* Fix regression where consecutive calls to `ModelSelect.select()` with no\n  parameters resulted in an empty selection. Fixes #1438.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.6...3.0.7)\n\n## 3.0.6\n\nAdd constraints for ON UPDATE/ON DELETE to foreign-key constraint - #1443.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.5...3.0.6)\n\n## 3.0.5\n\nAdds Model.index(), a short-hand method for declaring ModelIndex instances.\n\n* [Model.index docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.index)\n* [Model.add_index docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.add_index)\n* [ModelIndex docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#ModelIndex)\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.4...3.0.5)\n\n## 3.0.4\n\nRe-add a shim for `PrimaryKeyField` (renamed to `AutoField`) and log a\ndeprecation warning if you try to use it.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.3...3.0.4)\n\n## 3.0.3\n\nIncludes fix for bug where column-name to field-name translation was not being\ndone when running select queries on models whose field name differed from the\nunderlying column name (#1437).\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.2...3.0.3)\n\n## 3.0.2\n\nEnsures that the pysqlite headers are included in the source distribution so\nthat certain C extensions can be compiled.\n\n[View commits](https://github.com/coleifer/peewee/compare/3.0.0...3.0.2)\n\n## 3.0.0\n\n* Complete rewrite of SQL AST and code-generation.\n* Inclusion of new, low-level query builder APIs.\n* List of [backwards-incompatible changes](http://docs.peewee-orm.com/en/latest/peewee/changes.html).\n\n[View commits](https://github.com/coleifer/peewee/compare/2.10.2...3.0.0)\n\n## 2.10.2\n\n* Update travis-ci build scripts to use Postgres 9.6 and test against Python\n  3.6.\n* Added support for returning `namedtuple` objects when iterating over a\n  cursor.\n* Added support for specifying the \"object id\" attribute used when declaring a\n  foreign key. By default, it is `foreign-key-name_id`, but it can now be\n  customized.\n* Fixed small bug in the calculation of search scores when using the SQLite C\n  extension or the `sqlite_ext` module.\n* Support literal column names with the `dataset` module.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.10.1...2.10.2)\n\n## 2.10.1\n\nRemoved `AESEncryptedField`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.10.0...2.10.1)\n\n## 2.10.0\n\nThe main change in this release is the removal of the `AESEncryptedField`,\nwhich was included as part of the `playhouse.fields` extension. It was brought\nto my attention that there was some serious potential for security\nvulnerabilities. Rather than give users a false sense of security, I've decided\nthe best course of action is to remove the field.\n\n* Remove the `playhouse.fields.AESEncryptedField` over security concerns\ndescribed in ticket #1264.\n* Correctly resolve explicit table dependencies when creating tables, refs\n  #1076. Thanks @maaaks.\n* Implement not equals comparison for `CompositeKey`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.9.2...2.10.0)\n\n## 2.9.2\n\n* Fixed significant bug in the `savepoint` commit/rollback implementation. Many\n  thanks to @Syeberman for raising the issue. See #1225 for details.\n* Added support for postgresql `INTERVAL` columns. The new `IntervalField` in\n  the `postgres_ext` module is suitable for storing `datetime.timedelta`.\n* Fixed bug where missing `sqlite3` library was causing other, unrelated\n  libraries to throw errors when attempting to import.\n* Added a `case_sensitive` parameter to the SQLite `REGEXP` function\n  implementation. The default is `False`, to preserve backwards-compatibility.\n* Fixed bug that caused tables not to be created when using the `dataset`\n  extension. See #1213 for details.\n* Modified `drop_table` to raise an exception if the user attempts to drop\n  tables with `CASCADE` when the database backend does not support it.\n* Fixed Python3 issue in the `AESEncryptedField`.\n* Modified the behavior of string-typed fields to treat the addition operator\n  as concatenation. See #1241 for details.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.9.1...2.9.2)\n\n## 2.9.1\n\n* Fixed #1218, where the use of `playhouse.flask_utils` was requiring the\n  `sqlite3` module to be installed.\n* Fixed #1219 regarding the SQL generation for composite key sub-selects,\n  joins, etc.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.9.0...2.9.1)\n\n## 2.9.0\n\nIn this release there are two notable changes:\n\n* The ``Model.create_or_get()`` method was removed. See the [documentation](http://docs.peewee-orm.com/en/latest/peewee/querying.html#create-or-get)\n  for an example of the code one would write to replicate this functionality.\n* The SQLite closure table extension gained support for many-to-many\n  relationships thanks to a nice PR by @necoro. [Docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#ClosureTable).\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.8...2.9.0)\n\n## 2.8.8\n\nThis release contains a single important bugfix for a regression in specifying\nthe type of lock to use when opening a SQLite transaction.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.7...2.8.8)\n\n## 2.8.7\n\nThis release contains numerous cleanups.\n\n### Bugs fixed\n\n* #1087 - Fixed a misuse of the iteration protocol in the `sqliteq` extension.\n* Ensure that driver exceptions are wrapped when calling `commit` and\n  `rollback`.\n* #1096 - Fix representation of recursive foreign key relations when using the\n  `model_to_dict` helper.\n* #1126 - Allow `pskel` to be installed into `bin` directory.\n* #1105 - Added a `Tuple()` type to Peewee to enable expressing arbitrary\n  tuple expressions in SQL.\n* #1133 - Fixed bug in the conversion of objects to `Decimal` instances in the\n  `DecimalField`.\n* Fixed an issue renaming a unique foreign key in MySQL.\n* Remove the join predicate from CROSS JOINs.\n* #1148 - Ensure indexes are created when a column is added using a schema\n  migration.\n* #1165 - Fix bug where the primary key was being overwritten in queries using\n  the closure-table extension.\n\n### New stuff\n\n* Added properties to the `SqliteExtDatabase` to expose common `PRAGMA`\n  settings. For example, to set the cache size to 4MB, `db.cache_size = 1000`.\n* Clarified documentation on calling `commit()` or `rollback()` from within the\n  scope of an atomic block. [See docs](http://docs.peewee-orm.com/en/latest/peewee/transactions.html#transactions).\n* Allow table creation dependencies to be specified using new `depends_on` meta\n  option. Refs #1076.\n* Allow specification of the lock type used in SQLite transactions. Previously\n  this behavior was only present in `playhouse.sqlite_ext.SqliteExtDatabase`,\n  but it now exists in `peewee.SqliteDatabase`.\n* Added support for `CROSS JOIN` expressions in select queries.\n* Docs on how to implement [optimistic locking](http://docs.peewee-orm.com/en/latest/peewee/hacks.html#optimistic-locking).\n* Documented optional dependencies.\n* Generic support for specifying select queries as locking the selected rows\n  `FOR X`, e.g. `FOR UPDATE` or `FOR SHARE`.\n* Support for specifying the frame-of-reference in window queries, e.g.\n  specifying `UNBOUNDED PRECEDING`, etc. [See docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#Window).\n\n### Backwards-incompatible changes\n\n* As of 9e76c99, an `OperationalError` is raised if the user calls `connect()`\n  on an already-open Database object. Previously, the existing connection would\n  remain open and a new connection would overwrite it, making it impossible to\n  close the previous connection. If you find this is causing breakage in your\n  application, you can switch the `connect()` call to `get_conn()` which will\n  only open a connection if necessary. The error **is** indicative of a real\n  issue, though, so audit your code for places where you may be opening a\n  connection without closing it (module-scope operations, e.g.).\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.5...2.8.7)\n\n## 2.8.6\n\nThis release was later removed due to containing a bug. See notes on 2.8.7.\n\n## 2.8.5\n\nThis release contains two small bugfixes.\n\n* #1081 - fixed the use of parentheses in compound queries on MySQL.\n* Fixed some grossness in a helper function used by `prefetch` that was\n  clearing out the `GROUP BY` and `HAVING` clauses of sub-queries.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.4...2.8.5)\n\n## 2.8.4\n\nThis release contains bugfixes as well as a new playhouse extension module for\nworking with [SQLite in multi-threaded / concurrent environments](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#sqliteq).\nThe new module is called `playhouse.sqliteq` and it works by serializing\nqueries using a dedicated worker thread (or greenlet). The performance is quite\ngood, hopefully this proves useful to someone besides myself! You can learn\nmore by reading the [sqliteq documentation](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#sqliteq).\n\nAs a miscellaneous note, I did some major refactoring and cleanup in\n`ExtQueryResultsWrapper` and it's corollary in the `speedups` module. The code\nis much easier to read than before.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.3...2.8.4)\n\n### Bugs fixed\n\n* #1061 - @akrs patched a bug in `TimestampField` which affected the accuracy\n  of sub-second timestamps (for resolution > 1).\n* #1071, small python 3 fix.\n* #1072, allow `DeferredRelation` to be used multiple times if there are\n  multiple references to a given deferred model.\n* #1073, fixed regression in the speedups module that caused SQL functions to\n  always coerce return values, regardless of the `coerce` flag.\n* #1083, another Python 3 issue - this time regarding the use of `exc.message`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.3...2.8.4)\n\n## 2.8.3\n\nThis release contains bugfixes and a small backwards-incompatible change to the\nway foreign key `ObjectIdDescriptor` is named (issue #1050).\n\n### Bugs fixed and general changes\n\n* #1028 - allow the `ensure_join` method to accept `on` and `join_type`\n  parameters. Thanks @paulbooth.\n* #1032 - fix bug related to coercing model instances to database parameters\n  when the model's primary key is a foreign key.\n* #1035 - fix bug introduced in 2.8.2, where I had added some logic to try and\n  restrict the base `Model` class from being treated as a \"real\" Model.\n* #1039 - update documentation to clarify that lists *or tuples* are acceptable\n  values when specifying SQLite `PRAGMA` statements.\n* #1041 - PyPy user was unable to install Peewee. (Who in their right mind\n  would *ever* use PyPy?!) Bug was fixed by removing the pre-generated C files\n  from the distribution.\n* #1043 - fix bug where the `speedups` C extension was not calling the correct\n  model initialization method, resulting in model instances returned as results\n  of a query having their `dirty` flag incorrectly set.\n* #1048 - similar to #1043, add logic to ensure that fields with default values\n  are considered dirty when instantiating the model.\n* #1049 - update URL to [APSW](https://rogerbinns.github.io/apsw).\n* Fixed unreported bug regarding `TimestampField` with zero values reporting\n  the incorrect datetime.\n\n### New stuff\n\n* [djpeewee](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#djpeewee) extension\n  module now works with Django 1.9.\n* [TimestampField](http://docs.peewee-orm.com/en/latest/peewee/api.html#TimestampField)\n  is now an officially documented field.\n* #1050 - use the `db_column` of a `ForeignKeyField` for the name of the\n  `ObjectIdDescriptor`, except when the `db_column` and field `name` are the\n  same, in which case the ID descriptor will be named `<field_name>_id`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.2...2.8.3)\n\n## 2.8.2\n\nThis release contains mostly bug-fixes, clean-ups, and API enhancements.\n\n### Bugs fixed and general cleanups\n\n* #820 - fixed some bugs related to the Cython extension build process.\n* #858 - allow blanks and perform type conversion when using the `db_url`\n  extension\n* #922 - ensure that `peewee.OperationalError` is raised consistently when\n  using the `RetryOperationalError` mixin.\n* #929 - ensure that `pwiz` will import the appropriate extensions when\n  vendor-specific fields are used.\n* #930 - ensure that `pwiz`-generated models containing `UnknownField`\n  placeholders do not blow up when you instantiate them.\n* #932 - correctly limit the length of automatically-generated index names.\n* #933 - fixed bug where `BlobField` could not be used if it's parent model\n  pointed to an uninitialized database `Proxy`.\n* #935 - greater consistency with the conversion to Python data-types when\n  performing aggregations, annotations, or calling `scalar()`.\n* #939 - ensure the correct data-types are used when initializing a connection\n  pool.\n* #947 - fix bug where `Signal` subclasses were not returning rows affected on\n  save.\n* #951 - better warnings regarding C extension compilation, thanks @dhaase-de.\n* #968 - fix bug where table names starting with numbers generated invalid\n  table names when using `pwiz`.\n* #971 - fix bug where parameter was not being used. Thanks @jberkel.\n* #974 - fixed the way `SqliteExtDatabase` handles the automatic `rowid` (and\n    `docid`) columns. Thanks for alerting me to the issue and providing a\n    failing test case @jberkel.\n* #976 - fix obscure bug relating to cloning foreign key fields twice.\n* #981 - allow `set` instances to be used on the right-hand side of `IN` exprs.\n* #983 - fix behavior where the default `id` primary key was inherited\n  regardless. When users would inadvertently include it in their queries, it\n  would use the table alias of it's parent class.\n* #992 - add support for `db_column` in `djpeewee`\n* #995 - fix the behavior of `truncate_date` with Postgresql. Thanks @Zverik.\n* #1011 - correctly handle `bytes` wrapper used by `PasswordField` to `bytes`.\n* #1012 - when selecting and joining on multiple models, do not create model\n  instances when the foreign key is NULL.\n* #1017 - do not coerce the return value of function calls to `COUNT` or `SUM`,\n  since the python driver will already give us the right Python value.\n* #1018 - use global state to resolve `DeferredRelations`, allowing for a nicer\n  API. Thanks @brenguyen711.\n* #1022 - attempt to avoid creating invalid Python when using `pwiz` with MySQL\n  database columns containing spaces. Yes, fucking spaces.\n* #1024 - fix bug in SQLite migrator which had a naive approach to fixing\n  indexes.\n* #1025 - explicitly check for `None` when determining if the database has been\n  set on `ModelOptions`. Thanks @joeyespo.\n\n### New stuff\n\n* Added `TimestampField` for storing datetimes using integers. Greater than\n  second delay is possible through exponentiation.\n* Added `Database.drop_index()` method.\n* Added a `max_depth` parameter to the `model_to_dict` function in\n  the `playhouse.shortcuts` extension module.\n* `SelectQuery.first()` function accepts a parameter `n` which\n  applies a limit to the query and returns the first row. Previously the limit\n  was not applied out of consideration for subsequent iterations, but I believe\n  usage has shown that a limit is more desirable than reserving the option to\n  iterate without a second query. The old behavior is preserved in the new\n  `SelectQuery.peek()` method.\n* `group_by()`, `order_by()`, `window()` now accept a keyward argument\n  `extend`, which, when set to `True`, will append to the existing values\n  rather than overwriting them.\n* Query results support negative indexing.\n* C sources are included now as part of the package. I *think* they should be\n  able to compile for python 2 or 3, on linux or windows...but not positive.\n* #895 - added the ability to query using the `<foreign_key>_id` attribute.\n* #948 - added documentation about SQLite limits and how they affect\n* #1009 - allow `DATABASE_URL` as a recognized parameter to the Flask config.\n  `insert_many`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.1...2.8.2)\n\n## 2.8.1\n\nThis release is long overdue so apologies if you've been waiting on it and\nrunning off master. There are numerous bugfixes contained in this release, so\nI'll list those first this time.\n\n### Bugs fixed\n\n* #821 - issue warning if Cython is old\n* #822 - better handling of MySQL connections\npoint for advanced use-cases.\n* #313 - support equality/inequality with generic foreign key queries, and\nensure `get_or_create` works with GFKs.\n* #834 - fixed Python3 incompatibilities in the `PasswordField`, thanks\n@mosquito.\n* #836 - fix handling of `last_insert_id()` when using `APSWDatabase`.\n* #845 - add connection hooks to `APSWDatabase`.\n* #852 - check SQLite library version to avoid calls to missing APIs.\n* #857 - allow database definition to be deferred when using the connection\npool.\n* #878 - formerly `.limit(0)` had no effect. Now adds `LIMIT 0`.\n* #879 - implement a `__hash__` method for `Model`\n* #886 - fix `count()` for compound select queries.\n* #895 - allow writing to the `foreign_key_id` descriptor to set the foreign\nkey value.\n* #893 - fix boolean logic bug in `model_to_dict()`.\n* #904 - fix side-effect in `clean_prefetch_query`, thanks to @p.kamayev\n* #907 - package includes `pskel` now.\n* #852 - fix sqlite version check in BerkeleyDB backend.\n* #919 - add runtime check for `sqlite3` library to match MySQL and Postgres.\nThanks @M157q\n\n### New features\n\n* Added a number of [SQLite user-defined functions and\naggregates](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#sqlite-udf).\n* Use the DB-API2 `Binary` type for `BlobField`.\n* Implemented the lucene scoring algorithm in the `sqlite_ext` Cython library.\n* #825 - allow a custom base class for `ModelOptions`, providing an extension\n* #830 - added `SmallIntegerField` type.\n* #838 - allow using a custom descriptor class with `ManyToManyField`.\n* #855 - merged change from @lez which included docs on using peewee with\nPyramid.\n* #858 - allow arguments to be passed on query-string when using the `db_url`\nmodule. Thanks @RealSalmon\n* #862 - add support for `truncate table`, thanks @dev-zero for the sample\ncode.\n* Allow the `related_name` model `Meta` option to be a callable that accepts\nthe foreign key field instance.\n\n\n[View commits](https://github.com/coleifer/peewee/compare/2.8.0...2.8.1)\n\n## 2.8.0\n\nThis release includes a couple new field types and greatly improved C extension support for both speedups and SQLite enhancements. Also includes some work, suggested by @foxx, to remove some places where `Proxy` was used in favor of more obvious APIs.\n\n### New features\n\n* [travis-ci builds](http://travis-ci.org/coleifer/peewee/builds/) now include MySQL and Python 3.5. Dropped support for Python 3.2 and 3.3. Builds also will run the C-extension code.\n* C extension speedups now enabled by default, includes faster implementations for `dict` and `tuple` `QueryResultWrapper` classes, faster date formatting, and a faster field and model sorting.\n* C implementations of SQLite functions is now enabled by default. SQLite extension is now compatible with APSW and can be used in standalone form directly from Python. See [SqliteExtDatabase](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#SqliteExtDatabase) for more details.\n* SQLite C extension now supports `murmurhash2`.\n* `UUIDField` is now supported for SQLite and MySQL, using `text` and `varchar` respectively, thanks @foxx!\n* Added `BinaryField`, thanks again, @foxx!\n* Added `PickledField` to `playhouse.fields`.\n* `ManyToManyField` now accepts a list of primary keys when adding or removing values from the through relationship.\n* Added support for SQLite [table-valued functions](http://sqlite.org/vtab.html#tabfunc2) using the [sqlite-vtfunc library](https://github.com/coleifer/sqlite-vtfunc).\n* Significantly simplified the build process for compiling the C extensions.\n\n### Backwards-incompatible changes\n\n* Instead of using a `Proxy` for defining circular foreign key relationships, you now need to use [DeferredRelation](http://docs.peewee-orm.com/en/latest/peewee/api.html#DeferredRelation).\n* Instead of using a `Proxy` for defining many-to-many through tables, you now need to use [DeferredThroughModel](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#DeferredThroughModel).\n* SQLite Virtual Models must now use `Meta.extension_module` and `Meta.extension_options` to declare extension and any options. For more details, see [VirtualModel](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#VirtualModel).\n* MySQL database will now issue `COMMIT` statements for `SELECT` queries. This was not necessary, but added due to an influx of confused users creating GitHub tickets. Hint: learn to user your damn database, it's not magic!\n\n### Bugs fixed\n\nSome of these may have been included in a previous release, but since I did not list them I'm listing them here.\n\n* #766, fixed bug with PasswordField and Python3. Fuck Python 3.\n* #768, fixed SortedFieldList and `remove_field()`. Thanks @klen!\n* #771, clarified docs for APSW.\n* #773, added docs for request hooks in Pyramid (who uses Pyramid, by the way?).\n* #774, prefetch() only loads first ForeignKeyField for a given relation.\n* #782, fixed typo in docs.\n* #791, foreign keys were not correctly handling coercing to the appropriate python value.\n* #792, cleaned up some CSV utils code.\n* #798, cleaned up iteration protocol in QueryResultWrappers.\n* #806, not really a bug, but MySQL users were clowning around and needed help.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.7.4...2.8.0)\n\n## 2.7.4\n\nThis is another small release which adds code to automatically build the SQLite C extension if `libsqlite` is available. The release also includes:\n\n* Support for `UUIDField` with SQLite.\n* Support for registering additional database classes with the `db_url` module via `register_database`.\n* `prefetch()` supports fetching multiple foreign-keys to the same model class.\n* Added method to validate FTS5 search queries.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.7.3...2.7.4)\n\n## 2.7.3\n\nSmall release which includes some changes to the BM25 sorting algorithm and the addition of a [`JSONField`](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#JSONField) for use with the new [JSON1 extension](http://sqlite.org/json1.html).\n\n## 2.7.2\n\nPeople were having trouble building the sqlite extension. I figure enough people are having trouble that I made it a separate command: `python setup.py build_sqlite_ext`.\n\n## 2.7.1\n\nJacked up the setup.py\n\n## 2.7.0\n\nNew APIs, features, and performance improvements.\n\n### Notable changes and new features\n\n* [`PasswordField`](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#PasswordField) that uses the `bcrypt` module.\n* Added new Model [`Meta.only_save_dirty`](http://docs.peewee-orm.com/en/latest/peewee/models.html#model-options-and-table-metadata) flag to, by default, only save fields that have been modified.\n* Added support for [`upsert()`](http://docs.peewee-orm.com/en/latest/peewee/api.html#InsertQuery.upsert) on MySQL (in addition to SQLite).\n* Implemented SQLite ranking functions (``rank`` and ``bm25``) in Cython, and changed both the Cython and Python APIs to accept weight values for every column in the search index. This more closely aligns with the APIs provided by FTS5. In fact, made the APIs for FTS4 and FTS5 result ranking compatible.\n* Major changes to the :ref:`sqlite_ext` module. Function callbacks implemented in Python were implemented in Cython (e.g. date manipulation and regex processing) and will be used if Cython is available when Peewee is installed.\n* Support for the experimental new [FTS5](http://sqlite.org/fts5.html) SQLite search extension.\n* Added :py:class:`SearchField` for use with the SQLite FTS extensions.\n* Added :py:class:`RowIDField` for working with the special ``rowid`` column in SQLite.\n* Added a model class validation hook to allow model subclasses to perform any validation after class construction. This is currently used to ensure that ``FTS5Model`` subclasses do not violate any rules required by the FTS5 virtual table.\n\n### Bugs fixed\n\n* **#751**, fixed some very broken behavior in the MySQL migrator code. Added more tests.\n* **#718**, added a `RetryOperationalError` mixin that will try automatically reconnecting after a failed query. There was a bug in the previous error handler implementation that made this impossible, which is also fixed.\n\n#### Small bugs\n\n* #713, fix column name regular expression in SQLite migrator.\n* #724, fixed `NULL` handling with the Postgresql `JSONField`.\n* #725, added `__module__` attribute to `DoesNotExist` classes.\n* #727, removed the `commit_select` logic for MySQL databases.\n* #730, added documentation for `Meta.order_by` API.\n* #745, added `cast()` method for casting JSON field values.\n* #748, added docs and method override to indicate that SQLite does not support adding foreign key constraints after table creation.\n* Check whether pysqlite or libsqlite were compiled with BerkeleyDB support when using the :py:class:`BerkeleyDatabase`.\n* Clean up the options passed to SQLite virtual tables on creation.\n\n### Small features\n\n* #700, use sensible default if field's declared data-type is not present in the field type map.\n* #707, allow model to be specified explicitly in `prefetch()`.\n* #734, automatic testing against python 3.5.\n* #753, added support for `upsert()` ith MySQL via the `REPLACE INTO ...` statement.\n* #757, `pwiz`, the schema intropsection tool, will now generate multi-column index declarations.\n* #756, `pwiz` will capture passwords using the `getpass()` function rather than via the command-line.\n* Removed `Database.sql_error_handler()`, replaced with the `RetryOperationalError` mixin class.\n* Documentation for `Meta.order_by` and `Meta.primary_key`.\n* Better documentation around column and table constraints.\n* Improved performance for some methods that are called frequently.\n* Added `coerce` parameter to `BareField` and added documentation.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.6.4...2.7.0)\n\n\n## 2.6.4\n\nUpdating so some of the new APIs are available on pypi.\n\n### Bugs fixed\n\n* #646, fixed a bug with the Cython speedups not being included in package.\n* #654, documented how to create models with no primary key.\n* #659, allow bare `INSERT` statements.\n* #674, regarding foreign key / one-to-one relationships.\n* #676, allow `ArrayField` to accept tuples in addition to lists.\n* #679, fix regarding unsaved relations.\n* #682, refactored QueryResultWrapper to allow multiple independent iterations over the same underlying result cache.\n* #692, fix bug with multiple joins to same table + eager loading.\n* #695, fix bug when connection fails while using an execution context.\n* #698, use correct column names with non-standard django foreign keys.\n* #706, return `datetime.time` instead of `timedelta` for MySQL time fields.\n* #712, fixed SQLite migrator regular expressions. Thanks @sroebert.\n\n### New features\n\n* #647, #649, #650, added support for `RETURNING` clauses. Update, Insert and Delete queries can now be called with `RETURNING` to retrieve the rows that were affected. [See docs](http://docs.peewee-orm.com/en/latest/peewee/querying.html#returning-clause).\n* #685, added web request hook docs.\n* #691, allowed arbitrary model attributes and methods to be serialized by `model_to_dict()`. [Docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#model_to_dict).\n* #696, allow `model_to_dict()` to introspect query for which fields to serialize.\n* Added backend-agnostic [truncate_date()](http://docs.peewee-orm.com/en/latest/peewee/api.html#Database.truncate_date) implementation.\n* Added a `FixedCharField` which uses column type `CHAR`.\n* Added support for arbitrary `PRAGMA` statements to be run on new SQLite connections. [Docs](http://docs.peewee-orm.com/en/latest/peewee/databases.html#sqlite-pragma).\n* Removed `berkeley_build.sh` script. See instructions [on my blog instead](http://charlesleifer.com/blog/building-the-python-sqlite-driver-for-use-with-berkeleydb/).\n\n[View commits](https://github.com/coleifer/peewee/compare/2.6.2...2.6.4)\n\n## 2.6.2\n\nJust a regular old release.\n\n### Bugs fixed\n\n* #641, fixed bug with exception wrapping and Python 2.6\n* #634, fixed bug where correct query result wrapper was not being used for certain composite queries.\n* #625, cleaned up some example code.\n* #614, fixed bug with `aggregate_rows()` when there are multiple joins to the same table.\n\n### New features\n\n* Added [create_or_get()](http://docs.peewee-orm.com/en/latest/peewee/querying.html#create-or-get) as a companion to `get_or_create()`.\n* Added support for `ON CONFLICT` clauses for `UPDATE` and `INSERT` queries. [Docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#UpdateQuery.on_conflict).\n* Added a [JSONKeyStore](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#JSONKeyStore) to `playhouse.kv`.\n* Added Cythonized version of `strip_parens()`, with plans to perhaps move more performance-critical code to Cython in the future.\n* Added docs on specifying [vendor-specific database parameters](http://docs.peewee-orm.com/en/latest/peewee/database.html#vendor-specific-parameters).\n* Added docs on specifying [field default values](http://docs.peewee-orm.com/en/latest/peewee/models.html#default-field-values) (both client and server-side).\n* Added docs on [foreign key field back-references](http://docs.peewee-orm.com/en/latest/peewee/models.html#foreignkeyfield).\n* Added docs for [models without a primary key](http://docs.peewee-orm.com/en/latest/peewee/models.html#models-without-a-primary-key).\n* Cleaned up docs on `prefetch()` and `aggregate_rows()`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.6.1...2.6.2)\n\n## 2.6.1\n\nThis release contains a number of small fixes and enhancements.\n\n### Bugs fixed\n\n* #606, support self-referential joins with `prefetch` and `aggregate_rows()` methods.\n* #588, accomodate changes in SQLite's `PRAGMA index_list()` return value.\n* #607, fixed bug where `pwiz` was not passing table names to introspector.\n* #591, fixed bug with handling of named cursors in older psycopg2 version.\n* Removed some cruft from the `APSWDatabase` implementation.\n\n### New features\n\n* Added [CompressedField](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#CompressedField) and [AESEncryptedField](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#AESEncryptedField)\n* #609, #610, added Django-style foreign key ID lookup. [Docs](http://docs.peewee-orm.com/en/latest/peewee/models.html#foreignkeyfield).\n* Added support for [Hybrid Attributes](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#hybrid-attributes) (cool idea courtesy of SQLAlchemy).\n* Added ``upsert`` keyword argument to the `Model.save()` function (SQLite only).\n* #587, added support for ``ON CONFLICT`` SQLite clause for `INSERT` and `UPDATE` queries. [Docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#UpdateQuery.on_conflict)\n* #601, added hook for programmatically defining table names. [Model options docs](http://docs.peewee-orm.com/en/latest/peewee/models.html#model-options-and-table-metadata)\n* #581, #611, support connection pools with `playhouse.db_url.connect()`. [Docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#connect).\n* Added [Contributing section](http://docs.peewee-orm.com/en/latest/peewee/contributing.html) section to docs.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.6.0...2.6.1)\n\n## 2.6.0\n\nThis is a tiny update, mainly consisting of a new-and-improved implementation of ``get_or_create()`` ([docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.get_or_create)).\n\n### Backwards-incompatible changes\n\n* ``get_or_create()`` now returns a 2-tuple consisting of the model instance and a boolean indicating whether the instance was created. The function now behaves just like the Django equivalent.\n\n### New features\n\n* #574, better support for setting the character encoding on Postgresql database connections. Thanks @klen!\n* Improved implementation of [get_or_create()](http://docs.peewee-orm.com/en/latest/peewee/api.html#Model.get_or_create).\n\n[View commits](https://github.com/coleifer/peewee/compare/2.5.1...2.6.0)\n\n## 2.5.1\n\nThis is a relatively small release with a few important bugfixes.\n\n### Bugs fixed\n\n* #566, fixed a bug regarding parentheses around compound `SELECT` queries (i.e. `UNION`, `INTERSECT`, etc).\n* Fixed unreported bug where table aliases were not generated correctly for compound `SELECT` queries.\n* #559, add option to preserve original column order with `pwiz`. Thanks @elgow!\n* Fixed unreported bug where selecting all columns from a `ModelAlias` does not use the appropriate `FieldAlias` objects.\n\n### New features\n\n* #561, added an option for bulk insert queries to return the list of auto-generated primary keys. See [docs for InsertQuery.return_id_list](http://docs.peewee-orm.com/en/latest/peewee/api.html#InsertQuery.return_id_list).\n* #569, added `parse` function to the `playhouse.db_url` module. Thanks @stt!\n* Added [hacks](http://docs.peewee-orm.com/en/latest/peewee/hacks.html) section to the docs. Please contribute your hacks!\n\n### Backwards-incompatible changes\n\n* Calls to `Node.in_()` and `Node.not_in()` do not take `*args` anymore and instead take a single argument.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.5.0...2.5.1)\n\n## 2.5.0\n\nThere are a couple new features so I thought I'd bump to 2.5.x. One change Postgres users may be happy to see is the use of `INSERT ... RETURNING` to perform inserts. This should definitely speed up inserts for Postgres, since an extra query is no longer needed to get the new auto-generated primary key.\n\nI also added a [new context manager/decorator](http://docs.peewee-orm.com/en/latest/peewee/database.html#using-multiple-databases) that allows you to use a different database for the duration of the wrapped block.\n\n### Bugs fixed\n\n* #534, CSV utils was erroneously stripping the primary key from CSV data.\n* #537, fix upserts when using `insert_many`.\n* #541, respect `autorollback` with `PostgresqlExtDatabase`. Thanks @davidmcclure.\n* #551, fix for QueryResultWrapper's implementation of the iterator protocol.\n* #554, allow SQLite journal_mode to be set at run-time.\n* Fixed case-sensitivity issue with `DataSet`.\n\n### New features\n\n* Added support for [CAST expressions](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#cast).\n* Added a hook for [extending Node](http://docs.peewee-orm.com/en/latest/peewee/api.html#Node.extend) with custom methods.\n* `JOIN_<type>` became `JOIN.<type>`, e.g. `.join(JOIN.LEFT_OUTER)`.\n* `OP_<code>` became `OP.<code>`.\n* #556, allowed using `+` and `-` prefixes to indicate ascending/descending ordering.\n* #550, added [Database.initialize_connection()](http://docs.peewee-orm.com/en/latest/peewee/database.html#additional-connection-initialization) hook.\n* #549, bind selected columns to a particular model. Thanks @jhorman, nice PR!\n* #531, support for swapping databases at run-time via [Using](http://docs.peewee-orm.com/en/latest/peewee/database.html#using-multiple-databases).\n* #530, support for SQLCipher and Python3.\n* New `RowIDField` for `sqlite_ext` playhouse module. This field can be used to interact with SQLite `rowid` fields.\n* Added `LateralJoin` helper to the `postgres_ext` playhouse module.\n* New [example blog app](https://github.com/coleifer/peewee/tree/master/examples/blog).\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.7...2.5.0)\n\n## 2.4.7\n\n### Bugs fixed\n\n* #504, Docs updates.\n* #506, Fixed regression in `aggregate_rows()`\n* #510, Fixes bug in pwiz overwriting columns.\n* #514, Correctly cast foreign keys in `prefetch()`.\n* #515, Simplifies queries issued when doing recursive deletes.\n* #516, Fix cloning of Field objects.\n* #519, Aggregate rows now correctly preserves ordering of joined instances.\n* Unreported, fixed bug to not leave expired connections sitting around in the pool.\n\n### New features\n\n* Added support for Postgresql's ``jsonb`` type with [BinaryJSONField](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#BinaryJSONField).\n* Add some basic [Flask helpers](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#flask-utils).\n* Add support for `UNION ALL` queries in #512\n* Add `SqlCipherExtDatabase`, which combines the sqlcipher database with the sqlite extensions.\n* Add option to print metadata when generating code with ``pwiz``.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.6...2.4.7)\n\n## 2.4.6\n\nThis is a relatively small release with mostly bug fixes and updates to the documentation. The one new feature I'd like to highlight is the ``ManyToManyField`` ([docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#ManyToManyField)).\n\n### Bugs fixed\n\n* #503, fixes behavior of `aggregate_rows()` when used with a `CompositeKey`.\n* #498, fixes value coercion for field aliases.\n* #492, fixes bug with pwiz and composite primary keys.\n* #486, correctly handle schemas with reflection module.\n\n### New features\n\n* Peewee has a new [ManyToManyField](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#ManyToManyField) available in the ``playhouse.shortcuts`` module.\n* Peewee now has proper support for *NOT IN* queries through the ``Node.not_in()`` method.\n* Models now support iteration. This is equivalent to ``Model.select()``.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.5...2.4.6)\n\n## 2.4.5\n\nI'm excited about this release, as in addition to a number of new features and bugfixes, it also is a step towards cleaner code. I refactored the tests into a number of modules, using a standard set of base test-cases and helpers. I also introduced the `mock` library into the test suite and plan to use it for cleaner tests going forward. There's a lot of work to do to continue cleaning up the tests, but I'm feeling good about the changes. Curiously, the test suite runs faster now.\n\n### Bugs fixed\n\n* #471, #482 and #484, all of which had to do with how joins were handled by the `aggregate_rows()` query result wrapper.\n* #472 removed some needless special-casing in `Model.save()`.\n* #466 fixed case-sensitive issues with the SQLite migrator.\n* #474 fixed a handful of bugs that cropped up migrating foreign keys with SQLite.\n* #475 fixed the behavior of the SQLite migrator regarding auto-generated indexes.\n* #479 fixed a bug in the code that stripped extra parentheses in the SQL generator.\n* Fixed a handful of bugs in the APSW extension.\n\n### New features\n\n* Added connection abstraction called `ExecutionContext` ([see docs](http://docs.peewee-orm.com/en/latest/peewee/database.html#advanced-connection-management)).\n* Made all context managers work as decorators (`atomic`, `transaction`, `savepoint`, `execution_context`).\n* Added explicit methods for `IS NULL` and `IS NOT NULL` queries. The latter was actually necessary since the behavior is different from `NOT IS NULL (...)`.\n* Allow disabling backref validation (#465)\n* Made quite a few improvements to the documentation, particularly sections on transactions.\n* Added caching to the [DataSet](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#dataset) extension, which should improve performance.\n* Made the SQLite migrator smarter with regards to preserving indexes when a table copy is necessary.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.4...2.4.5)\n\n## 2.4.4\n\nBiggest news: peewee has a new logo!\n\n![](https://media.charlesleifer.com/blog/photos/peewee-logo-bold.png)\n\n* Small documentation updates here and there.\n\n### Backwards-incompatible changes\n\n* The argument signature for the `SqliteExtDatabase.aggregate()` decorator changed so that the aggregate name is the first parameter, and the number of parameters is the second parameter. If no values are specified, peewee will choose the name of the class and an un-specified number of arguments (`-1`).\n* The logic for saving a model with a composite key changed slightly. Previously, if a model had a composite primary key and you called `save()`, only the dirty fields would be saved.\n\n### Bugs fixed\n\n* #462\n* #465, add hook for disabling backref validation.\n* #466, fix case-sensitive table names with migration module.\n* #469, save only dirty fields.\n\n### New features\n\n* Lots of enhancements and cleanup to the `playhouse.apsw_ext` module.\n* The `playhouse.reflection` module now supports introspecting indexes.\n* Added a model option for disabling backref validation.\n* Added support for the SQLite [closure table extension](http://charlesleifer.com/blog/querying-tree-structures-in-sqlite-using-python-and-the-transitive-closure-extension/).\n* Added support for *virtual fields*, which act on dynamically-created virtual table fields.\n* Added a new example: a virtual table implementation that exposes Redis as a relational database table.\n* Added a module `playhouse.sqlite_aggregates` that contains a handful of aggregates you may find useful when developing with SQLite.\n\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.3...2.4.4)\n\n## 2.4.3\n\nThis release contains numerous improvements, particularly around the built-in database introspection utilities. Peewee should now also be compatible with PyPy.\n\n### Bugs fixed\n\n* #466, table names are case sensitive in the SQLite migrations module.\n* #465, added option to disable backref validation.\n* #462, use the schema name consistently with postgres reflection.\n\n### New features\n\n* New model *Meta* option to disable backref validation. [See validate_backrefs](http://docs.peewee-orm.com/en/latest/peewee/models.html#model-options-and-table-metadata).\n* Added documentation on ordering by calculated values.\n* Added basic PyPy compatibility.\n* Added logic to close cursors after they have been exhausted.\n* Structured and consolidated database metadata introspection, including improvements for introspecting indexes.\n* Added support to [prefetch](http://docs.peewee-orm.com/en/latest/peewee/api.html?highlight=prefetch#prefetch) for traversing *up* the query tree.\n* Added introspection option to skip invalid models while introspecting.\n* Added option to limit the tables introspected.\n* Added closed connection detection to the MySQL connection pool.\n* Enhancements to passing options to creating virtual tables with SQLite.\n* Added factory method for generating Closure tables for use with the `transitive_closure` SQLite extension.\n* Added support for loading SQLite extensions.\n* Numerous test-suite enhancements and new test-cases.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.2...2.4.3)\n\n## 2.4.2\n\nThis release contains a number of improvements to the `reflection` and `migrate` extension modules. I also added an encrypted *diary* app to the [examples](https://github.com/coleifer/peewee/tree/master/examples) directory.\n\n### Bugs fixed\n\n* #449, typo in the db_url extension, thanks to @malea for the fix.\n* #457 and #458, fixed documentation deficiences.\n\n### New features\n\n* Added support for [importing data](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#importing-data) when using the [DataSet extension](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#dataset).\n* Added an encrypted diary app to the examples.\n* Better index reconstruction when altering columns on SQLite databases with the [migrate](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#migrate) module.\n* Support for multi-column primary keys in the [reflection](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#reflection) module.\n* Close cursors more aggressively when executing SELECT queries.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.1...2.4.2)\n\n## 2.4.1\n\nThis release contains a few small bugfixes.\n\n### Bugs fixed\n\n* #448, add hook to the connection pool for detecting closed connections.\n* #229, fix join attribute detection.\n* #447, fixed documentation typo.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.4.0...2.4.1)\n\n## 2.4.0\n\nThis release contains a number of enhancements to the `playhouse` collection of extensions.\n\n### Backwards-incompatible changes\n\nAs of 2.4.0, most of the introspection logic was moved out of the ``pwiz`` module and into ``playhouse.reflection``.\n\n### New features\n\n* Created a new [reflection](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#reflection) extension for introspecting databases. The *reflection* module additionally can generate actual peewee Model classes dynamically.\n* Created a [dataset](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#dataset) library (based on the [SQLAlchemy project](https://dataset.readthedocs.io/) of the same name). For more info check out the blog post [announcing playhouse.dataset](http://charlesleifer.com/blog/saturday-morning-hacks-dataset-for-peewee/).\n* Added a [db_url](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#database-url) module which creates `Database` objects from a connection string.\n* Added [csv dump](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#dumping-csv) functionality to the [CSV utils](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#csv-utils) extension.\n* Added an [atomic](http://docs.peewee-orm.com/en/latest/peewee/transactions.html#nesting-transactions) context manager to support nested transactions.\n* Added support for HStore, JSON and TSVector to the `reflection` module.\n* More documentation updates.\n\n### Bugs fixed\n\n* Fixed #440, which fixes a bug where `Model.dirty_fields` did not return an empty set for some subclasses of `QueryResultWrapper`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.3.3...2.4.0)\n\n## 2.3.3\n\nThis release contains a lot of improvements to the documentation and a mixed bag of other new features and bugfixes.\n\n### Backwards-incompatible changes\n\nAs of 2.3.3, all peewee `Database` instances have a default of `True` for the `threadlocals` parameter. This means that a connection is opened for each thread. It seemed to me that by sharing connections across threads caused a lot of confusion to users who weren't aware of (or familiar with) the `threadlocals` parameter. For single-threaded apps the behavior will not be affected, but for multi-threaded applications, if you wish to share your connection across threads you must now specify `threadlocals=False`. For more information, see the [documentation](http://docs.peewee-orm.com/en/latest/peewee/api.html#Database).\n\nI also renamed the `Model.get_id()` and `Model.set_id()` convenience methods so as not to conflict with Flask-Login. These methods should have probably been private anyways, and the new methods are named `_get_pk_value()` and `_set_pk_value()`.\n\n### New features\n\n* Basic support for [Postgresql full-text search](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#pg-fts).\n* Helper functions for converting models to dictionaries and unpacking dictionaries into model instances. See [docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#model_to_dict).\n\n### Bugs fixed\n\n* Fixed #428, documentation formatting error.\n* Fixed #429, which fixes the way default values are initialized for bulk inserts.\n* Fixed #432, making the HStore extension optional when using `PostgresqlExtDatabase`.\n* Fixed #435, allowing peewee to be used with Flask-Login.\n* Fixed #436, allowing the SQLite date_part and date_trunc functions to correctly handle NULL values.\n* Fixed #438, in which the ordering of clauses in a Join expression were causing unpredictable behavior when selecting related instances.\n* Updated the `berkeley_build.sh` script, which was incompatible with the newest version of `bsddb3`.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.3.2...2.3.3)\n\n## 2.3.2\n\nThis release contains mostly bugfixes.\n\n### Changes in 2.3.2\n\n* Fixed #421, allowing division operations to work correctly in py3k.\n* Added support for custom json.dumps command, thanks to @alexlatchford.\n* Fixed some foreign key generation bugs with pwiz in #426.\n* Fixed a parentheses bug with UNION queries, #422.\n* Added support for returning partial JSON data-structures from postgresql.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.3.1...2.3.2)\n\n## 2.3.1\n\nThis release contains a fix for a bug introducted in 2.3.0. Table names are included, unquoted, in update queries now, which is causing some problems when the table name is a keyword.\n\n### Changes in 2.3.1\n\n* [Quote table name / alias](https://github.com/coleifer/peewee/issues/414)\n\n[View commits](https://github.com/coleifer/peewee/compare/2.3.0...2.3.1)\n\n## 2.3.0\n\nThis release contains a number of bugfixes, enhancements and a rewrite of much of the documentation.\n\n### Changes in 2.3.0\n\n* [New and improved documentation](http://docs.peewee-orm.com/)\n* Added [aggregate_rows()](http://docs.peewee-orm.com/en/latest/peewee/querying.html#list-users-and-all-their-tweets) method for mitigating N+1 queries.\n* Query compiler performance improvements and rewrite of table alias internals (51d82fcd and d8d55df04).\n* Added context-managers and decorators for [counting queries](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#count_queries) and [asserting query counts](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#assert_query_count).\n* Allow `UPDATE` queries to contain subqueries for values ([example](http://docs.peewee-orm.com/en/latest/peewee/querying.html#atomic-updates)).\n* Support for `INSERT INTO / SELECT FROM` queries ([docs](http://docs.peewee-orm.com/en/latest/peewee/api.html?highlight=insert_from#Model.insert_from)).\n* Allow `SqliteDatabase` to set the database's journal mode.\n* Added method for concatenation ([docs]()).\n* Moved ``UUIDField`` out of the playhouse and into peewee\n* Added [pskel](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#pskel) script.\n* Documentation for [BerkeleyDB](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#berkeleydb).\n\n### Bugs fixed\n\n* #340, allow inner query values to be used in outer query joins.\n* #380, fixed foreign key handling in SQLite migrations.\n* #389, mark foreign keys as dirty on assignment.\n* #391, added an ``orwhere()`` method.\n* #392, fixed ``order_by`` meta option inheritance bug.\n* #394, fixed UUID and conversion of foreign key values (thanks @alexlatchford).\n* #395, allow selecting all columns using ``SQL('*')``.\n* #396, fixed query compiler bug that was adding unnecessary parentheses around expressions.\n* #405, fixed behavior of ``count()`` when query has a limit or offset.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.2.5...2.3.0)\n\n## 2.2.5\n\nThis is a small release and contains a handful of fixes.\n\n### Changes in 2.2.5\n\n* Added a `Window` object for creating reusable window definitions.\n* Added support for `DISTINCT ON (...)`.\n* Added a BerkeleyDB-backed sqlite `Database` and build script.\n* Fixed how the `UUIDField` handles `None` values (thanks @alexlatchford).\n* Fixed various things in the example app.\n* Added 3.4 to the travis build (thanks @frewsxcv).\n\n[View commits](https://github.com/coleifer/peewee/compare/2.2.4...2.2.5)\n\n## 2.2.4\n\nThis release contains a complete rewrite of `pwiz` as well as some improvements to the SQLite extension, including support for the BM25 ranking algorithm for full-text searches. I also merged support for sqlcipher, an encrypted SQLite database with many thanks to @thedod!\n\n### Changes in 2.2.4\n\n* Rewrite of `pwiz`, schema introspection utility.\n* `Model.save()` returns a value indicating the number of modified rows.\n* Fixed bug with `PostgresqlDatabase.last_insert_id()` leaving a transaction open in autocommit mode (#353).\n* Added BM25 ranking algorithm for full-text searches with SQLite.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.2.3...2.2.4)\n\n## 2.2.3\n\nThis release contains a new migrations module in addition to a number of small features and bug fixes.\n\n### Changes in 2.2.3\n\n* New migrations module.\n* Added a return value to `Model.save()` indicating number of rows affected.\n* Added a `date_trunc()` method that works for Sqlite.\n* Added a `Model.sqlall()` class-method to return all the SQL to generate the model / indices.\n\n### Bugs fixed\n\n* #342, allow functions to not coerce parameters automatically.\n* #338, fixed unaliased columns when using Array and Json fields with postgres, thanks @mtwesley.\n* #331, corrected issue with the way unicode arrays were adapted with psycopg2.\n* #328, pwiz / mysql bug.\n* #326, fixed calculation of the alias_map when using subqueries.\n* #324, bug with `prefetch()` not selecting the correct primary key.\n\n\n[View commits](https://github.com/coleifer/peewee/compare/2.2.2...2.2.3)\n\n\n## 2.2.1\n\nI've been looking forward to this release, as it contains a couple new features\nthat I've been wanting to add for some time now. Hope you find them useful.\n\n### Changes in 2.2.1\n\n* Window queries using ``OVER`` syntax.\n* Compound query operations ``UNION``, ``INTERSECT``, ``EXCEPT`` as well as symmetric difference.\n\n### Bugs fixed\n\n* #300, pwiz was not correctly interpreting some foreign key constraints in SQLite.\n* #298, drop table with cascade API was missing.\n* #294, typo.\n\n[View commits](https://github.com/coleifer/peewee/compare/2.2.0...2.2.1)\n\n## 2.2.0\n\nThis release contains a large refactoring of the way SQL was generated for both\nthe standard query classes (`Select`, `Insert`, `Update`, `Delete`) as well as\nfor the DDL methods (`create_table`, `create_index`, etc). Instead of joining\nstrings of SQL and manually quoting things, I've created `Clause` objects\ncontaining multiple `Node` objects to represent all parts of the query.\n\nI also changed the way peewee determins the SQL to represent a field. Now a\nfield implements ``__ddl__`` and ``__ddl_column__`` methods. The former creates\nthe entire field definition, e.g.:\n\n    \"quoted_column_name\" <result of call to __ddl_column__> [NOT NULL/PRIMARY KEY/DEFAULT NEXTVAL(...)/CONSTRAINTS...]\n\nThe latter method is responsible just for the column type definition. This might\nreturn ``VARCHAR(255)`` or simply ``TEXT``. I've also added support for\narbitrary constraints on each field, so you might have:\n\n    price = DecimalField(decimal_places=2, constraints=[Check('price > 0')])\n\n### Changes in 2.2.0\n\n* Refactored query generation for both SQL queries and DDL queries.\n* Support for arbitrary column constraints.\n* `autorollback` option to the `Database` class that will roll back the\n  transaction before raising an exception.\n* Added `JSONField` type to the `postgresql_ext` module.\n* Track fields that are explicitly set, allowing faster saves (thanks @soasme).\n* Allow the `FROM` clause to be an arbitrary `Node` object (#290).\n* `schema` is a new `Model.Mketa` option and is used throughout the code.\n* Allow indexing operation on HStore fields (thanks @zdxerr, #293).\n\n### Bugs fixed\n\n* #277 (where calls not chainable with update query)\n* #278, use `wraps()`, thanks @lucasmarshall\n* #284, call `prepared()` after `create()`, thanks @soasme.\n* #286, cursor description issue with pwiz + postgres\n\n[View commits](https://github.com/coleifer/peewee/compare/2.1.7...2.2.0)\n\n\n## 2.1.7\n\n### Changes in 2.1.7\n\n* Support for savepoints (Sqlite, Postgresql and MySQL) using an API similar to that of transactions.\n* Common set of exceptions to wrap DB-API 2 driver-specific exception classes, e.g. ``peewee.IntegrityError``.\n* When pwiz cannot determine the underlying column type, display it in a comment in the generated code.\n* Support for circular foreign-keys.\n* Moved ``Proxy`` into peewee (previously in ``playhouse.proxy``).\n* Renamed ``R()`` to ``SQL()``.\n* General code cleanup, some new comments and docstrings.\n\n### Bugs fixed\n\n* Fixed a small bug in the way errors were handled in transaction context manager.\n* #257\n* #265, nest multiple calls to functions decorated with `@database.commit_on_success`.\n* #266\n* #267\n\nCommits: https://github.com/coleifer/peewee/compare/2.1.6...2.1.7\nReleased 2013-12-25\n\n## 2.1.6\n\nChanges included in 2.1.6:\n\n* [Lightweight Django integration](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#django-integration).\n* Added a [csv loader](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#csv-loader) to playhouse.\n* Register unicode converters per-connection instead of globally when using `pscyopg2`.\n* Fix for how the related object cache is invalidated (#243).\n\nCommits: https://github.com/coleifer/peewee/compare/2.1.5...2.1.6\nReleased 2013-11-19\n\n## 2.1.5\n\n### Summary of new features\n\n* Rewrote the ``playhouse.postgres_ext.ServerSideCursor`` helper to work with a single query.  [Docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#server-side-cursors).\n* Added error handler hook to the database class, allowing your code to choose how to handle errors executing SQL.  [Docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#Database.sql_error_handler).\n* Allow arbitrary attributes to be stored in ``Model.Meta`` a5e13bb26d6196dbd24ff228f99ff63d9c046f79.\n* Support for composite primary keys (!!).  [How-to](http://docs.peewee-orm.com/en/latest/peewee/cookbook.html#composite-primary-keys) and [API docs](http://docs.peewee-orm.com/en/latest/peewee/api.html#CompositeKey).\n* Added helper for generating ``CASE`` expressions.  [Docs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#case).\n* Allow the table alias to be specified as a model ``Meta`` option.\n* Added ability to specify ``NOWAIT`` when issuing ``SELECT FOR UPDATE`` queries.\n\n### Bug fixes\n\n* #147, SQLite auto-increment behavior.\n* #222\n* #223, missing call to ``execute()`` in docs.\n* #224, python 3 compatibility fix.\n* #227, was using wrong column type for boolean with MySQL.\n\nCommits: https://github.com/coleifer/peewee/compare/2.1.4...2.1.5\nReleased 2013-10-19\n\n## 2.1.4\n\n* Small refactor of some components used to represent expressions (mostly better names).\n* Support for [Array fields](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#ArrayField) in postgresql.\n* Added notes on [Proxy](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#proxy)\n* Support for [Server side cursors](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#server-side-cursors) with postgresql.\n* Code cleanups for more consistency.\n\nCommits: https://github.com/coleifer/peewee/compare/2.1.3...2.1.4\nReleased 2013-08-05\n\n## 2.1.3\n\n* Added the ``sqlite_ext`` module, including support for virtual tables, full-text search, user-defined functions, collations and aggregates, as well as more granular locking.\n* Manually convert data-types when doing simple aggregations - fixes issue #208\n* Profiled code and dramatically increased performance of benchmarks.\n* Added a proxy object for lazy database initialization - fixes issue #210\n\nCommits: https://github.com/coleifer/peewee/compare/2.1.2...2.1.3\nReleased 2013-06-28\n\n-------------------------------------\n\n## 2.0.0\n\nMajor rewrite, see notes here: http://docs.peewee-orm.com/en/latest/peewee/upgrading.html#upgrading\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2010 Charles Leifer\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include CHANGELOG.md\ninclude LICENSE\ninclude README.rst\ninclude TODO.rst\ninclude pyproject.toml\ninclude runtests.py\ninclude tests.py\ninclude playhouse/*.c\ninclude playhouse/*.pyx\ninclude playhouse/README.md\nrecursive-include examples *\nrecursive-include docs *\n"
  },
  {
    "path": "README.rst",
    "content": ".. image:: https://media.charlesleifer.com/blog/photos/peewee4-logo.png\n\npeewee\n======\n\nPeewee is a simple and small ORM. It has few (but expressive) concepts, making it easy to learn and intuitive to use.\n\n* a small, expressive ORM\n* flexible query-builder that exposes full power of SQL\n* supports sqlite, mysql, mariadb, postgresql\n* asyncio support\n* tons of extensions\n* use with `flask <https://docs.peewee-orm.com/en/latest/peewee/framework_integration.html#flask>`__,\n  `fastapi <https://docs.peewee-orm.com/en/latest/peewee/framework_integration.html#fastapi>`__,\n  `pydantic <https://docs.peewee-orm.com/en/latest/peewee/orm_utils.html#module-playhouse.pydantic_utils>`__, and\n  `more <https://docs.peewee-orm.com/en/latest/peewee/framework_integration.html>`__.\n\nNew to peewee? These may help:\n\n* `Quickstart <https://docs.peewee-orm.com/en/latest/peewee/quickstart.html#quickstart>`_\n* `Example twitter app <https://docs.peewee-orm.com/en/latest/peewee/example.html#example>`_\n* `Using peewee interactively <https://docs.peewee-orm.com/en/latest/peewee/interactive.html#interactive>`_\n* `Models and fields <http://docs.peewee-orm.com/en/latest/peewee/models.html>`_\n* `Querying <http://docs.peewee-orm.com/en/latest/peewee/querying.html>`_\n* `Relationships and joins <http://docs.peewee-orm.com/en/latest/peewee/relationships.html>`_\n* `Extensive library of SQL / Peewee examples <https://docs.peewee-orm.com/en/latest/peewee/query_library.html#query-library>`_\n* `Flask setup <https://docs.peewee-orm.com/en/latest/peewee/framework_integration.html#flask>`_\n  or `FastAPI setup <https://docs.peewee-orm.com/en/latest/peewee/framework_integration.html#fastapi>`_\n\nInstallation:\n\n.. code-block:: console\n\n    pip install peewee\n\nSqlite comes built-in provided by the standard-lib ``sqlite3`` module. Other\nbackends can be installed using the following instead:\n\n.. code-block:: console\n\n    pip install peewee[mysql]  # Install peewee with pymysql.\n    pip install peewee[postgres]  # Install peewee with psycopg2.\n    pip install peewee[psycopg3]  # Install peewee with psycopg3.\n\n    # AsyncIO implementations.\n    pip install peewee[aiosqlite]  # Install peewee with aiosqlite.\n    pip install peewee[aiomysql]  # Install peewee with aiomysql.\n    pip install peewee[asyncpg]  # Install peewee with asyncpg.\n\nExamples\n--------\n\nDefining models is similar to Django or SQLAlchemy:\n\n.. code-block:: python\n\n    from peewee import *\n    import datetime\n\n\n    db = SqliteDatabase('my_database.db')\n\n    class BaseModel(Model):\n        class Meta:\n            database = db\n\n    class User(BaseModel):\n        username = CharField(unique=True)\n\n    class Tweet(BaseModel):\n        user = ForeignKeyField(User, backref='tweets')\n        message = TextField()\n        created_date = DateTimeField(default=datetime.datetime.now)\n        is_published = BooleanField(default=True)\n\nConnect to the database and create tables:\n\n.. code-block:: python\n\n    db.connect()\n    db.create_tables([User, Tweet])\n\nCreate a few rows:\n\n.. code-block:: python\n\n    charlie = User.create(username='charlie')\n    huey = User(username='huey')\n    huey.save()\n\n    # No need to set `is_published` or `created_date` since they\n    # will just use the default values we specified.\n    Tweet.create(user=charlie, message='My first tweet')\n\nQueries are expressive and composable:\n\n.. code-block:: python\n\n    # A simple query selecting a user.\n    User.get(User.username == 'charlie')\n\n    # Get tweets created by one of several users.\n    usernames = ['charlie', 'huey', 'mickey']\n    users = User.select().where(User.username.in_(usernames))\n    tweets = Tweet.select().where(Tweet.user.in_(users))\n\n    # We could accomplish the same using a JOIN:\n    tweets = (Tweet\n              .select()\n              .join(User)\n              .where(User.username.in_(usernames)))\n\n    # How many tweets were published today?\n    tweets_today = (Tweet\n                    .select()\n                    .where(\n                        (Tweet.created_date >= datetime.date.today()) &\n                        (Tweet.is_published == True))\n                    .count())\n\n    # Paginate the user table and show me page 3 (users 41-60).\n    User.select().order_by(User.username).paginate(3, 20)\n\n    # Order users by the number of tweets they've created:\n    tweet_ct = fn.Count(Tweet.id)\n    users = (User\n             .select(User, tweet_ct.alias('ct'))\n             .join(Tweet, JOIN.LEFT_OUTER)\n             .group_by(User)\n             .order_by(tweet_ct.desc()))\n\n    # Do an atomic update (for illustrative purposes only, imagine a simple\n    # table for tracking a \"count\" associated with each URL). We don't want to\n    # naively get the save in two separate steps since this is prone to race\n    # conditions.\n    Counter.update(count=Counter.count + 1).where(Counter.url == request.url)\n\nCheck out the `example twitter app <http://docs.peewee-orm.com/en/latest/peewee/example.html>`_.\n\nLearning more\n-------------\n\nCheck the `documentation <http://docs.peewee-orm.com/>`_ for more examples.\n\nSpecific question? Come hang out in the #peewee channel on irc.libera.chat, or post to the mailing list, http://groups.google.com/group/peewee-orm . If you would like to report a bug, `create a new issue <https://github.com/coleifer/peewee/issues/new>`_ on GitHub.\n\nStill want more info?\n---------------------\n\n.. image:: https://media.charlesleifer.com/blog/photos/wat.jpg\n\nI've written a number of blog posts about building applications and web-services with peewee (and usually Flask). If you'd like to see some real-life applications that use peewee, the following resources may be useful:\n\n* `Building a note-taking app with Flask and Peewee <https://charlesleifer.com/blog/saturday-morning-hack-a-little-note-taking-app-with-flask/>`_ as well as `Part 2 <https://charlesleifer.com/blog/saturday-morning-hacks-revisiting-the-notes-app/>`_ and `Part 3 <https://charlesleifer.com/blog/saturday-morning-hacks-adding-full-text-search-to-the-flask-note-taking-app/>`_.\n* `Analytics web service built with Flask and Peewee <https://charlesleifer.com/blog/saturday-morning-hacks-building-an-analytics-app-with-flask/>`_.\n* `Personalized news digest (with a boolean query parser!) <https://charlesleifer.com/blog/saturday-morning-hack-personalized-news-digest-with-boolean-query-parser/>`_.\n* `Structuring Flask apps with Peewee <https://charlesleifer.com/blog/structuring-flask-apps-a-how-to-for-those-coming-from-django/>`_.\n* `Creating a lastpass clone with Flask and Peewee <https://charlesleifer.com/blog/creating-a-personal-password-manager/>`_.\n* `Creating a bookmarking web-service that takes screenshots of your bookmarks <https://charlesleifer.com/blog/building-bookmarking-service-python-and-phantomjs/>`_.\n* `Building a pastebin, wiki and a bookmarking service using Flask and Peewee <https://charlesleifer.com/blog/dont-sweat-small-stuff-use-flask-blueprints/>`_.\n* `Encrypted databases with Python and SQLCipher <https://charlesleifer.com/blog/encrypted-sqlite-databases-with-python-and-sqlcipher/>`_.\n* `Dear Diary: An Encrypted, Command-Line Diary with Peewee <https://charlesleifer.com/blog/dear-diary-an-encrypted-command-line-diary-with-python/>`_.\n"
  },
  {
    "path": "bench.py",
    "content": "from peewee import *\n\n\ndb = SqliteDatabase(':memory:')\n#db = PostgresqlDatabase('peewee_test', host='127.0.0.1', port=26257, user='root')\n#db = PostgresqlDatabase('peewee_test', host='127.0.0.1', user='postgres')\n#from playhouse.cysqlite_ext import CySqliteDatabase\n#db = CySqliteDatabase(':memory:')\n#from playhouse.apsw_ext import APSWDatabase\n#db = APSWDatabase(':memory:')\n\nclass Base(Model):\n    class Meta:\n        database = db\n\nclass Register(Base):\n    value = IntegerField()\n\nclass Collection(Base):\n    name = TextField()\n\nclass Item(Base):\n    collection = ForeignKeyField(Collection, backref='items')\n    name = TextField()\n\nimport functools\nimport time\n\ndef timed(fn):\n    @functools.wraps(fn)\n    def inner(*args, **kwargs):\n        times = []\n        N = 20\n        for i in range(N):\n            start = time.perf_counter()\n            fn(i, *args, **kwargs)\n            times.append(time.perf_counter() - start)\n        print('%0.3f ... %s' % (round(sum(times) / N, 3), fn.__name__))\n    return inner\n\ndef populate_register(s, n):\n    for i in range(s, n):\n        Register.create(value=i)\n\ndef populate_collections(n, n_i):\n    for i in range(n):\n        c = Collection.create(name=str(i))\n        for j in range(n_i):\n            Item.create(collection=c, name=str(j))\n\n@timed\ndef insert(i):\n    with db.atomic():\n        populate_register((i * 1000), (i + 1) * 1000)\n\n@timed\ndef batch_insert(i):\n    it = range(i * 1000, (i + 1) * 1000)\n    for i in db.batch_commit(it, 100):\n        Register.insert(value=i).execute()\n\n@timed\ndef bulk_insert(i):\n    with db.atomic():\n        for i in range(i * 1000, (i + 1) * 1000, 100):\n            data = [(j,) for j in range(i, i + 100)]\n            Register.insert_many(data, fields=[Register.value]).execute()\n\n@timed\ndef bulk_create(i):\n    with db.atomic():\n        data = [Register(value=i) for i in range(i * 1000, (i + 1) * 1000)]\n        Register.bulk_create(data, batch_size=100)\n\n@timed\ndef select(i):\n    query = Register.select()\n    for row in query:\n        pass\n\n@timed\ndef select_related_dbapi_raw(i):\n    query = Item.select(Item, Collection).join(Collection)\n    cursor = db.execute(query)\n    for row in cursor:\n        pass\n\n@timed\ndef insert_related(i):\n    with db.atomic():\n        populate_collections(30, 60)\n\n@timed\ndef select_related(i):\n    query = Item.select(Item, Collection).join(Collection)\n    for item in query:\n        pass\n\n@timed\ndef select_related_left(i):\n    query = Collection.select(Collection, Item).join(Item, JOIN.LEFT_OUTER)\n    for collection in query:\n        pass\n\n@timed\ndef select_related_dicts(i):\n    query = Item.select(Item, Collection).join(Collection).dicts()\n    for row in query:\n        pass\n\n@timed\ndef select_related_objects(i):\n    query = Item.select(Item, Collection).join(Collection).objects()\n    for item in query:\n        pass\n\n@timed\ndef select_prefetch(i):\n    query = prefetch(Collection.select(), Item)\n    for c in query:\n        for i in c.items:\n            pass\n\n@timed\ndef select_prefetch_join(i):\n    query = prefetch(Collection.select(), Item,\n                     prefetch_type=PREFETCH_TYPE.JOIN)\n    for c in query:\n        for i in c.items:\n            pass\n\n\nif __name__ == '__main__':\n    db.create_tables([Register, Collection, Item])\n    insert()\n    insert_related()\n    Register.delete().execute()\n    batch_insert()\n    assert Register.select().count() == 20000\n    Register.delete().execute()\n    bulk_insert()\n    assert Register.select().count() == 20000\n    Register.delete().execute()\n    bulk_create()\n    assert Register.select().count() == 20000\n    select()\n    select_related()\n    select_related_left()\n    select_related_objects()\n    select_related_dicts()\n    select_related_dbapi_raw()\n    select_prefetch()\n    select_prefetch_join()\n    db.drop_tables([Register, Collection, Item])\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build\nPAPER         =\nBUILDDIR      = _build\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\nclean:\n\t-rm -rf $(BUILDDIR)/*\n\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/peewee.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/peewee.qhc\"\n\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/peewee\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/peewee\"\n\t@echo \"# devhelp\"\n\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\tmake -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n"
  },
  {
    "path": "docs/_themes/flask/layout.html",
    "content": "{%- extends \"basic/layout.html\" %}\n{%- block extrahead %}\n  {{ super() }}\n  {% if theme_touch_icon %}\n  <link rel=\"apple-touch-icon\" href=\"{{ pathto('_static/' ~ theme_touch_icon, 1) }}\" />\n  {% endif %}\n  <link media=\"only screen and (max-device-width: 480px)\" href=\"{{\n    pathto('_static/small_flask.css', 1) }}\" type= \"text/css\" rel=\"stylesheet\" />\n{% endblock %}\n{%- block relbar2 %}{% endblock %}\n{% block header %}\n  {{ super() }}\n  {% if pagename == 'index' %}\n  <div class=indexwrapper>\n  {% endif %}\n{% endblock %}\n{%- block footer %}\n  <div class=\"footer\">\n    &copy; Copyright {{ copyright }}.\n    Created using <a href=\"http://sphinx.pocoo.org/\">Sphinx</a>.\n  </div>\n  {% if pagename == 'index' %}\n  </div>\n  {% endif %}\n{%- endblock %}\n"
  },
  {
    "path": "docs/_themes/flask/relations.html",
    "content": "<h3>Related Topics</h3>\n<ul>\n  <li><a href=\"{{ pathto(master_doc) }}\">Documentation overview</a><ul>\n  {%- for parent in parents %}\n  <li><a href=\"{{ parent.link|e }}\">{{ parent.title }}</a><ul>\n  {%- endfor %}\n    {%- if prev %}\n      <li>Previous: <a href=\"{{ prev.link|e }}\" title=\"{{ _('previous chapter')\n        }}\">{{ prev.title }}</a></li>\n    {%- endif %}\n    {%- if next %}\n      <li>Next: <a href=\"{{ next.link|e }}\" title=\"{{ _('next chapter')\n        }}\">{{ next.title }}</a></li>\n    {%- endif %}\n  {%- for parent in parents %}\n  </ul></li>\n  {%- endfor %}\n  </ul></li>\n</ul>\n"
  },
  {
    "path": "docs/_themes/flask/static/flasky.css_t",
    "content": "/*\n * flasky.css_t\n * ~~~~~~~~~~~~\n *\n * :copyright: Copyright 2010 by Armin Ronacher.\n * :license: Flask Design License, see LICENSE for details.\n */\n\n{% set page_width = '940px' %}\n{% set sidebar_width = '220px' %}\n \n@import url(\"basic.css\");\n \n/* -- page layout ----------------------------------------------------------- */\n \nbody {\n    font-family: 'Georgia', serif;\n    font-size: 17px;\n    background-color: white;\n    color: #000;\n    margin: 0;\n    padding: 0;\n}\n\ndiv.document {\n    width: {{ page_width }};\n    margin: 30px auto 0 auto;\n}\n\ndiv.documentwrapper {\n    float: left;\n    width: 100%;\n}\n\ndiv.bodywrapper {\n    margin: 0 0 0 {{ sidebar_width }};\n}\n\ndiv.sphinxsidebar {\n    width: {{ sidebar_width }};\n}\n\nhr {\n    border: 1px solid #B1B4B6;\n}\n \ndiv.body {\n    background-color: #ffffff;\n    color: #3E4349;\n    padding: 0 30px 0 30px;\n}\n\nimg.floatingflask {\n    padding: 0 0 10px 10px;\n    float: right;\n}\n \ndiv.footer {\n    width: {{ page_width }};\n    margin: 20px auto 30px auto;\n    font-size: 14px;\n    color: #888;\n    text-align: right;\n}\n\ndiv.footer a {\n    color: #888;\n}\n\ndiv.related {\n    display: none;\n}\n \ndiv.sphinxsidebar a {\n    color: #444;\n    text-decoration: none;\n    border-bottom: 1px dotted #999;\n}\n\ndiv.sphinxsidebar a:hover {\n    border-bottom: 1px solid #999;\n}\n \ndiv.sphinxsidebar {\n    font-size: 14px;\n    line-height: 1.5;\n}\n\ndiv.sphinxsidebarwrapper {\n    padding: 18px 10px;\n}\n\ndiv.sphinxsidebarwrapper p.logo {\n    padding: 0 0 20px 0;\n    margin: 0;\n    text-align: center;\n}\n \ndiv.sphinxsidebar h3,\ndiv.sphinxsidebar h4 {\n    font-family: 'Garamond', 'Georgia', serif;\n    color: #444;\n    font-size: 24px;\n    font-weight: normal;\n    margin: 0 0 5px 0;\n    padding: 0;\n}\n\ndiv.sphinxsidebar h4 {\n    font-size: 20px;\n}\n \ndiv.sphinxsidebar h3 a {\n    color: #444;\n}\n\ndiv.sphinxsidebar p.logo a,\ndiv.sphinxsidebar h3 a,\ndiv.sphinxsidebar p.logo a:hover,\ndiv.sphinxsidebar h3 a:hover {\n    border: none;\n}\n \ndiv.sphinxsidebar p {\n    color: #555;\n    margin: 10px 0;\n}\n\ndiv.sphinxsidebar ul {\n    margin: 10px 0;\n    padding: 0;\n    color: #000;\n}\n \ndiv.sphinxsidebar input {\n    border: 1px solid #ccc;\n    font-family: 'Georgia', serif;\n    font-size: 1em;\n}\n \n/* -- body styles ----------------------------------------------------------- */\n \na {\n    color: #004B6B;\n    text-decoration: underline;\n}\n \na:hover {\n    color: #6D4100;\n    text-decoration: underline;\n}\n \ndiv.body h1,\ndiv.body h2,\ndiv.body h3,\ndiv.body h4,\ndiv.body h5,\ndiv.body h6 {\n    font-family: 'Garamond', 'Georgia', serif;\n    font-weight: normal;\n    margin: 30px 0px 10px 0px;\n    padding: 0;\n}\n\n{% if theme_index_logo %}\ndiv.indexwrapper h1 {\n    text-indent: -999999px;\n    background: url({{ theme_index_logo }}) no-repeat center center;\n    height: {{ theme_index_logo_height }};\n}\n{% endif %}\n \ndiv.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }\ndiv.body h2 { font-size: 180%; }\ndiv.body h3 { font-size: 150%; }\ndiv.body h4 { font-size: 130%; }\ndiv.body h5 { font-size: 100%; }\ndiv.body h6 { font-size: 100%; }\n \na.headerlink {\n    color: #ddd;\n    padding: 0 4px;\n    text-decoration: none;\n}\n \na.headerlink:hover {\n    color: #444;\n    background: #eaeaea;\n}\n \ndiv.body p, div.body dd, div.body li {\n    line-height: 1.4em;\n}\n\ndiv.admonition {\n    background: #fafafa;\n    margin: 20px -30px;\n    padding: 10px 30px;\n    border-top: 1px solid #ccc;\n    border-bottom: 1px solid #ccc;\n}\n\ndiv.admonition tt.xref, div.admonition a tt {\n    border-bottom: 1px solid #fafafa;\n}\n\ndd div.admonition {\n    margin-left: -60px;\n    padding-left: 60px;\n}\n\ndiv.admonition p.admonition-title {\n    font-family: 'Garamond', 'Georgia', serif;\n    font-weight: normal;\n    font-size: 24px;\n    margin: 0 0 10px 0;\n    padding: 0;\n    line-height: 1;\n}\n\ndiv.admonition p.last {\n    margin-bottom: 0;\n}\n\ndiv.highlight {\n    background-color: white;\n}\n\ndt:target, .highlight {\n    background: #FAF3E8;\n}\n\ndiv.note {\n    background-color: #eee;\n    border: 1px solid #ccc;\n}\n \ndiv.seealso {\n    background-color: #ffc;\n    border: 1px solid #ff6;\n}\n \ndiv.topic {\n    background-color: #eee;\n}\n \np.admonition-title {\n    display: inline;\n}\n \np.admonition-title:after {\n    content: \":\";\n}\n\npre, tt {\n    font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;\n    font-size: 0.9em;\n}\n\nimg.screenshot {\n}\n\ntt.descname, tt.descclassname {\n    font-size: 0.95em;\n}\n\ntt.descname {\n    padding-right: 0.08em;\n}\n\nimg.screenshot {\n    -moz-box-shadow: 2px 2px 4px #eee;\n    -webkit-box-shadow: 2px 2px 4px #eee;\n    box-shadow: 2px 2px 4px #eee;\n}\n\ntable.docutils {\n    border: 1px solid #888;\n    -moz-box-shadow: 2px 2px 4px #eee;\n    -webkit-box-shadow: 2px 2px 4px #eee;\n    box-shadow: 2px 2px 4px #eee;\n}\n\ntable.docutils td, table.docutils th {\n    border: 1px solid #888;\n    padding: 0.25em 0.7em;\n}\n\ntable.field-list, table.footnote {\n    border: none;\n    -moz-box-shadow: none;\n    -webkit-box-shadow: none;\n    box-shadow: none;\n}\n\ntable.footnote {\n    margin: 15px 0;\n    width: 100%;\n    border: 1px solid #eee;\n    background: #fdfdfd;\n    font-size: 0.9em;\n}\n\ntable.footnote + table.footnote {\n    margin-top: -15px;\n    border-top: none;\n}\n\ntable.field-list th {\n    padding: 0 0.8em 0 0;\n}\n\ntable.field-list td {\n    padding: 0;\n}\n\ntable.footnote td.label {\n    width: 0px;\n    padding: 0.3em 0 0.3em 0.5em;\n}\n\ntable.footnote td {\n    padding: 0.3em 0.5em;\n}\n\ndl {\n    margin: 0;\n    padding: 0;\n}\n\ndl dd {\n    margin-left: 30px;\n}\n\nblockquote {\n    margin: 0 0 0 30px;\n    padding: 0;\n}\n\nul, ol {\n    margin: 10px 0 10px 30px;\n    padding: 0;\n}\n \npre {\n    background: #eee;\n    padding: 7px 30px;\n    margin: 15px -30px;\n    line-height: 1.3em;\n}\n\ndl pre, blockquote pre, li pre {\n    margin-left: -60px;\n    padding-left: 60px;\n}\n\ndl dl pre {\n    margin-left: -90px;\n    padding-left: 90px;\n}\n \ntt {\n    background-color: #ecf0f3;\n    color: #222;\n    /* padding: 1px 2px; */\n}\n\ntt.xref, a tt {\n    background-color: #FBFBFB;\n    border-bottom: 1px solid white;\n}\n\na.reference {\n    text-decoration: none;\n    border-bottom: 1px dotted #004B6B;\n}\n\na.reference:hover {\n    border-bottom: 1px solid #6D4100;\n}\n\na.footnote-reference {\n    text-decoration: none;\n    font-size: 0.7em;\n    vertical-align: top;\n    border-bottom: 1px dotted #004B6B;\n}\n\na.footnote-reference:hover {\n    border-bottom: 1px solid #6D4100;\n}\n\na:hover tt {\n    background: #EEE;\n}\n"
  },
  {
    "path": "docs/_themes/flask/static/small_flask.css",
    "content": "/*\n * small_flask.css_t\n * ~~~~~~~~~~~~~~~~~\n *\n * :copyright: Copyright 2010 by Armin Ronacher.\n * :license: Flask Design License, see LICENSE for details.\n */\n\nbody {\n    margin: 0;\n    padding: 20px 30px;\n}\n\ndiv.documentwrapper {\n    float: none;\n    background: white;\n}\n\ndiv.sphinxsidebar {\n    display: block;\n    float: none;\n    width: 102.5%;\n    margin: 50px -30px -20px -30px;\n    padding: 10px 20px;\n    background: #333;\n    color: white;\n}\n\ndiv.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,\ndiv.sphinxsidebar h3 a {\n    color: white;\n}\n\ndiv.sphinxsidebar a {\n    color: #aaa;\n}\n\ndiv.sphinxsidebar p.logo {\n    display: none;\n}\n\ndiv.document {\n    width: 100%;\n    margin: 0;\n}\n\ndiv.related {\n    display: block;\n    margin: 0;\n    padding: 10px 0 20px 0;\n}\n\ndiv.related ul,\ndiv.related ul li {\n    margin: 0;\n    padding: 0;\n}\n\ndiv.footer {\n    display: none;\n}\n\ndiv.bodywrapper {\n    margin: 0;\n}\n\ndiv.body {\n    min-height: 0;\n    padding: 0;\n}\n"
  },
  {
    "path": "docs/_themes/flask/theme.conf",
    "content": "[theme]\ninherit = basic\nstylesheet = flasky.css\npygments_style = flask_theme_support.FlaskyStyle\n\n[options]\nindex_logo = ''\nindex_logo_height = 120px\ntouch_icon = \n"
  },
  {
    "path": "docs/clubdata.sql",
    "content": "--\n-- PostgreSQL database dump\n--\n--CREATE DATABASE exercises;\n--\\c exercises\n--CREATE SCHEMA cd;\n\n\n\n-- Dumped from database version 9.2.0\n-- Dumped by pg_dump version 9.2.0\n-- Started on 2013-05-19 16:05:10 BST\n\nSET client_encoding = 'UTF8';\nSET standard_conforming_strings = on;\nSET check_function_bodies = false;\nSET client_min_messages = warning;\n\n--\n-- TOC entry 171 (class 1259 OID 32818)\n-- Name: bookings; Type: TABLE; Schema: cd; Owner: -; Tablespace:\n--\n\nCREATE TABLE bookings (\n    bookid integer NOT NULL,\n    facid integer NOT NULL,\n    memid integer NOT NULL,\n    starttime timestamp without time zone NOT NULL,\n    slots integer NOT NULL\n);\n\n\n--\n-- TOC entry 169 (class 1259 OID 32770)\n-- Name: facilities; Type: TABLE; Schema: cd; Owner: -; Tablespace:\n--\n\nCREATE TABLE facilities (\n    facid integer NOT NULL,\n    name character varying(100) NOT NULL,\n    membercost numeric NOT NULL,\n    guestcost numeric NOT NULL,\n    initialoutlay numeric NOT NULL,\n    monthlymaintenance numeric NOT NULL\n);\n\n\n--\n-- TOC entry 170 (class 1259 OID 32800)\n-- Name: members; Type: TABLE; Schema: cd; Owner: -; Tablespace:\n--\n\nCREATE TABLE members (\n    memid integer NOT NULL,\n    surname character varying(200) NOT NULL,\n    firstname character varying(200) NOT NULL,\n    address character varying(300) NOT NULL,\n    zipcode integer NOT NULL,\n    telephone character varying(20) NOT NULL,\n    recommendedby integer,\n    joindate timestamp without time zone NOT NULL\n);\n\n\n--\n-- TOC entry 2202 (class 0 OID 32818)\n-- Dependencies: 171\n-- Data for Name: bookings; Type: TABLE DATA; Schema: cd; Owner: -\n--\n\nINSERT INTO bookings (bookid, facid, memid, starttime, slots) VALUES\n(0, 3, 1, '2012-07-03 11:00:00', 2),\n(1, 4, 1, '2012-07-03 08:00:00', 2),\n(2, 6, 0, '2012-07-03 18:00:00', 2),\n(3, 7, 1, '2012-07-03 19:00:00', 2),\n(4, 8, 1, '2012-07-03 10:00:00', 1),\n(5, 8, 1, '2012-07-03 15:00:00', 1),\n(6, 0, 2, '2012-07-04 09:00:00', 3),\n(7, 0, 2, '2012-07-04 15:00:00', 3),\n(8, 4, 3, '2012-07-04 13:30:00', 2),\n(9, 4, 0, '2012-07-04 15:00:00', 2),\n(10, 4, 0, '2012-07-04 17:30:00', 2),\n(11, 6, 0, '2012-07-04 12:30:00', 2),\n(12, 6, 0, '2012-07-04 14:00:00', 2),\n(13, 6, 1, '2012-07-04 15:30:00', 2),\n(14, 7, 2, '2012-07-04 14:00:00', 2),\n(15, 8, 2, '2012-07-04 12:00:00', 1),\n(16, 8, 3, '2012-07-04 18:00:00', 1),\n(17, 1, 0, '2012-07-05 17:30:00', 3),\n(18, 2, 1, '2012-07-05 09:30:00', 3),\n(19, 3, 3, '2012-07-05 09:00:00', 2),\n(20, 3, 1, '2012-07-05 19:00:00', 2),\n(21, 4, 3, '2012-07-05 18:30:00', 2),\n(22, 6, 0, '2012-07-05 13:00:00', 2),\n(23, 6, 1, '2012-07-05 14:30:00', 2),\n(24, 7, 2, '2012-07-05 18:30:00', 2),\n(25, 8, 3, '2012-07-05 12:30:00', 1),\n(26, 0, 0, '2012-07-06 08:00:00', 3),\n(27, 0, 0, '2012-07-06 14:00:00', 3),\n(28, 0, 2, '2012-07-06 15:30:00', 3),\n(29, 2, 1, '2012-07-06 17:00:00', 3),\n(30, 3, 1, '2012-07-06 11:00:00', 2),\n(31, 4, 3, '2012-07-06 12:00:00', 2),\n(32, 6, 1, '2012-07-06 14:00:00', 2),\n(33, 7, 2, '2012-07-06 08:30:00', 2),\n(34, 7, 2, '2012-07-06 13:30:00', 2),\n(35, 8, 3, '2012-07-06 15:30:00', 1),\n(36, 0, 2, '2012-07-07 08:30:00', 3),\n(37, 0, 0, '2012-07-07 12:30:00', 3),\n(38, 0, 2, '2012-07-07 14:30:00', 3),\n(39, 1, 3, '2012-07-07 08:30:00', 3),\n(40, 2, 1, '2012-07-07 09:00:00', 3),\n(41, 2, 1, '2012-07-07 11:30:00', 3),\n(42, 2, 1, '2012-07-07 16:00:00', 3),\n(43, 3, 2, '2012-07-07 12:30:00', 2),\n(44, 4, 3, '2012-07-07 11:30:00', 2),\n(45, 4, 3, '2012-07-07 14:00:00', 2),\n(46, 4, 0, '2012-07-07 17:30:00', 2),\n(47, 6, 0, '2012-07-07 08:30:00', 2),\n(48, 6, 1, '2012-07-07 10:30:00', 2),\n(49, 6, 1, '2012-07-07 14:30:00', 2),\n(50, 6, 0, '2012-07-07 16:00:00', 2),\n(51, 7, 2, '2012-07-07 11:30:00', 2),\n(52, 8, 3, '2012-07-07 16:00:00', 1),\n(53, 8, 3, '2012-07-07 17:30:00', 2),\n(54, 0, 3, '2012-07-08 13:00:00', 3),\n(55, 0, 2, '2012-07-08 17:30:00', 3),\n(56, 1, 1, '2012-07-08 15:00:00', 3),\n(57, 1, 1, '2012-07-08 17:30:00', 3),\n(58, 3, 1, '2012-07-08 11:30:00', 2),\n(59, 3, 3, '2012-07-08 18:30:00', 2),\n(60, 3, 1, '2012-07-08 19:30:00', 2),\n(61, 4, 0, '2012-07-08 11:00:00', 2),\n(62, 4, 2, '2012-07-08 16:30:00', 2),\n(63, 4, 0, '2012-07-08 18:00:00', 2),\n(64, 4, 0, '2012-07-08 19:30:00', 2),\n(65, 6, 0, '2012-07-08 14:00:00', 2),\n(66, 6, 0, '2012-07-08 18:30:00', 2),\n(67, 7, 2, '2012-07-08 11:00:00', 2),\n(68, 7, 1, '2012-07-08 16:30:00', 2),\n(69, 8, 3, '2012-07-08 10:00:00', 1),\n(70, 8, 3, '2012-07-08 16:30:00', 1),\n(71, 0, 2, '2012-07-09 12:30:00', 3),\n(72, 0, 2, '2012-07-09 15:30:00', 3),\n(73, 0, 2, '2012-07-09 19:00:00', 3),\n(74, 1, 0, '2012-07-09 13:00:00', 3),\n(75, 1, 1, '2012-07-09 19:00:00', 3),\n(76, 2, 1, '2012-07-09 09:00:00', 6),\n(77, 2, 0, '2012-07-09 19:00:00', 3),\n(78, 3, 3, '2012-07-09 17:00:00', 2),\n(79, 3, 3, '2012-07-09 18:30:00', 2),\n(80, 4, 2, '2012-07-09 11:00:00', 2),\n(81, 4, 3, '2012-07-09 14:30:00', 2),\n(82, 6, 0, '2012-07-09 14:30:00', 2),\n(83, 7, 1, '2012-07-09 15:30:00', 2),\n(84, 7, 0, '2012-07-09 18:30:00', 4),\n(85, 8, 3, '2012-07-09 09:30:00', 1),\n(86, 8, 3, '2012-07-09 16:30:00', 1),\n(87, 8, 3, '2012-07-09 20:00:00', 1),\n(88, 0, 0, '2012-07-10 11:30:00', 3),\n(89, 0, 0, '2012-07-10 16:00:00', 3),\n(90, 3, 2, '2012-07-10 08:00:00', 2),\n(91, 3, 1, '2012-07-10 11:00:00', 2),\n(92, 3, 3, '2012-07-10 15:30:00', 2),\n(93, 3, 2, '2012-07-10 16:30:00', 2),\n(94, 3, 1, '2012-07-10 18:00:00', 2),\n(95, 4, 0, '2012-07-10 10:00:00', 2),\n(96, 4, 4, '2012-07-10 11:30:00', 2),\n(97, 4, 0, '2012-07-10 15:00:00', 2),\n(98, 4, 3, '2012-07-10 17:00:00', 4),\n(99, 5, 0, '2012-07-10 08:30:00', 2),\n(100, 6, 0, '2012-07-10 14:30:00', 2),\n(101, 6, 0, '2012-07-10 19:00:00', 2),\n(102, 7, 4, '2012-07-10 08:30:00', 2),\n(103, 7, 2, '2012-07-10 17:30:00', 2),\n(104, 8, 0, '2012-07-10 11:30:00', 1),\n(105, 8, 3, '2012-07-10 12:00:00', 1),\n(106, 8, 3, '2012-07-10 19:30:00', 1),\n(107, 0, 4, '2012-07-11 08:00:00', 3),\n(108, 0, 2, '2012-07-11 10:00:00', 3),\n(109, 0, 0, '2012-07-11 12:00:00', 3),\n(110, 0, 0, '2012-07-11 14:00:00', 3),\n(111, 0, 2, '2012-07-11 15:30:00', 3),\n(112, 0, 2, '2012-07-11 18:30:00', 3),\n(113, 1, 0, '2012-07-11 12:30:00', 3),\n(114, 1, 0, '2012-07-11 16:00:00', 3),\n(115, 4, 1, '2012-07-11 08:00:00', 2),\n(116, 4, 0, '2012-07-11 09:00:00', 2),\n(117, 4, 3, '2012-07-11 11:00:00', 2),\n(118, 4, 0, '2012-07-11 15:00:00', 2),\n(119, 5, 4, '2012-07-11 17:00:00', 2),\n(120, 6, 0, '2012-07-11 14:00:00', 2),\n(121, 6, 0, '2012-07-11 19:30:00', 2),\n(122, 7, 0, '2012-07-11 08:00:00', 2),\n(123, 7, 0, '2012-07-11 14:00:00', 2),\n(124, 7, 0, '2012-07-11 16:30:00', 2),\n(125, 8, 4, '2012-07-11 11:00:00', 1),\n(126, 8, 3, '2012-07-11 13:00:00', 1),\n(127, 0, 0, '2012-07-12 13:30:00', 3),\n(128, 0, 2, '2012-07-12 16:30:00', 3),\n(129, 1, 1, '2012-07-12 11:30:00', 3),\n(130, 2, 1, '2012-07-12 09:00:00', 3),\n(131, 2, 1, '2012-07-12 18:30:00', 3),\n(132, 3, 3, '2012-07-12 18:00:00', 2),\n(133, 4, 1, '2012-07-12 16:00:00', 2),\n(134, 6, 0, '2012-07-12 12:00:00', 4),\n(135, 7, 2, '2012-07-12 08:00:00', 2),\n(136, 7, 4, '2012-07-12 13:30:00', 2),\n(137, 7, 4, '2012-07-12 16:00:00', 2),\n(138, 8, 3, '2012-07-12 16:30:00', 1),\n(139, 0, 2, '2012-07-13 10:30:00', 3),\n(140, 0, 4, '2012-07-13 14:00:00', 3),\n(141, 0, 3, '2012-07-13 17:00:00', 3),\n(142, 1, 1, '2012-07-13 15:00:00', 3),\n(143, 2, 1, '2012-07-13 09:00:00', 3),\n(144, 2, 0, '2012-07-13 15:00:00', 3),\n(145, 2, 1, '2012-07-13 16:30:00', 3),\n(146, 4, 0, '2012-07-13 11:00:00', 2),\n(147, 4, 0, '2012-07-13 13:30:00', 2),\n(148, 4, 0, '2012-07-13 15:00:00', 2),\n(149, 4, 3, '2012-07-13 16:00:00', 2),\n(150, 4, 4, '2012-07-13 17:30:00', 2),\n(151, 6, 0, '2012-07-13 09:30:00', 2),\n(152, 7, 0, '2012-07-13 08:00:00', 2),\n(153, 7, 1, '2012-07-13 11:00:00', 2),\n(154, 7, 4, '2012-07-13 12:30:00', 2),\n(155, 8, 0, '2012-07-13 15:30:00', 1),\n(156, 8, 2, '2012-07-13 18:30:00', 1),\n(157, 0, 2, '2012-07-14 08:30:00', 3),\n(158, 0, 4, '2012-07-14 11:30:00', 3),\n(159, 0, 3, '2012-07-14 15:00:00', 3),\n(160, 1, 3, '2012-07-14 10:30:00', 3),\n(161, 1, 3, '2012-07-14 12:30:00', 3),\n(162, 1, 0, '2012-07-14 14:30:00', 3),\n(163, 2, 1, '2012-07-14 08:30:00', 3),\n(164, 3, 2, '2012-07-14 16:00:00', 2),\n(165, 4, 3, '2012-07-14 08:00:00', 2),\n(166, 4, 1, '2012-07-14 14:30:00', 2),\n(167, 6, 0, '2012-07-14 09:30:00', 2),\n(168, 6, 1, '2012-07-14 12:30:00', 2),\n(169, 6, 0, '2012-07-14 15:00:00', 2),\n(170, 7, 2, '2012-07-14 12:30:00', 2),\n(171, 7, 2, '2012-07-14 15:00:00', 2),\n(172, 7, 4, '2012-07-14 16:30:00', 2),\n(173, 7, 1, '2012-07-14 19:00:00', 2),\n(174, 8, 3, '2012-07-14 09:00:00', 1),\n(175, 8, 1, '2012-07-14 17:00:00', 1),\n(176, 0, 2, '2012-07-15 08:00:00', 3),\n(177, 0, 0, '2012-07-15 16:00:00', 3),\n(178, 0, 2, '2012-07-15 19:00:00', 3),\n(179, 1, 0, '2012-07-15 10:00:00', 3),\n(180, 1, 0, '2012-07-15 12:00:00', 3),\n(181, 1, 3, '2012-07-15 15:30:00', 3),\n(182, 2, 1, '2012-07-15 13:00:00', 3),\n(183, 3, 1, '2012-07-15 17:30:00', 2),\n(184, 4, 3, '2012-07-15 11:30:00', 2),\n(185, 4, 0, '2012-07-15 15:00:00', 2),\n(186, 4, 3, '2012-07-15 17:30:00', 2),\n(187, 7, 4, '2012-07-15 14:30:00', 2),\n(188, 7, 4, '2012-07-15 17:00:00', 2),\n(189, 8, 4, '2012-07-15 10:00:00', 1),\n(190, 8, 2, '2012-07-15 12:00:00', 1),\n(191, 8, 3, '2012-07-15 12:30:00', 1),\n(192, 8, 3, '2012-07-15 13:30:00', 1),\n(193, 0, 5, '2012-07-16 11:00:00', 3),\n(194, 0, 5, '2012-07-16 19:00:00', 3),\n(195, 1, 1, '2012-07-16 08:00:00', 3),\n(196, 1, 0, '2012-07-16 12:30:00', 3),\n(197, 2, 1, '2012-07-16 16:30:00', 3),\n(198, 4, 3, '2012-07-16 09:00:00', 2),\n(199, 4, 1, '2012-07-16 11:00:00', 2),\n(200, 4, 3, '2012-07-16 12:00:00', 2),\n(201, 4, 3, '2012-07-16 17:30:00', 2),\n(202, 6, 0, '2012-07-16 18:30:00', 2),\n(203, 7, 4, '2012-07-16 08:00:00', 2),\n(204, 7, 2, '2012-07-16 11:30:00', 2),\n(205, 7, 4, '2012-07-16 12:30:00', 2),\n(206, 7, 5, '2012-07-16 14:00:00', 2),\n(207, 8, 4, '2012-07-16 12:00:00', 1),\n(208, 8, 1, '2012-07-16 15:00:00', 1),\n(209, 8, 4, '2012-07-16 18:00:00', 1),\n(210, 8, 3, '2012-07-16 19:30:00', 1),\n(211, 0, 5, '2012-07-17 12:30:00', 3),\n(212, 0, 5, '2012-07-17 18:00:00', 3),\n(213, 1, 1, '2012-07-17 10:00:00', 3),\n(214, 1, 4, '2012-07-17 14:30:00', 3),\n(215, 2, 5, '2012-07-17 10:30:00', 3),\n(216, 2, 1, '2012-07-17 12:30:00', 3),\n(217, 2, 1, '2012-07-17 15:30:00', 3),\n(218, 2, 2, '2012-07-17 19:00:00', 3),\n(219, 3, 1, '2012-07-17 14:00:00', 2),\n(220, 3, 2, '2012-07-17 15:00:00', 2),\n(221, 4, 0, '2012-07-17 09:00:00', 2),\n(222, 4, 3, '2012-07-17 10:30:00', 2),\n(223, 4, 3, '2012-07-17 12:00:00', 2),\n(224, 4, 5, '2012-07-17 16:00:00', 2),\n(225, 4, 3, '2012-07-17 18:30:00', 2),\n(226, 5, 0, '2012-07-17 13:30:00', 2),\n(227, 6, 4, '2012-07-17 12:00:00', 2),\n(228, 6, 0, '2012-07-17 14:00:00', 2),\n(229, 7, 4, '2012-07-17 08:00:00', 2),\n(230, 7, 5, '2012-07-17 14:00:00', 2),\n(231, 7, 4, '2012-07-17 16:00:00', 2),\n(232, 8, 3, '2012-07-17 08:30:00', 1),\n(233, 8, 2, '2012-07-17 11:00:00', 1),\n(234, 8, 3, '2012-07-17 11:30:00', 1),\n(235, 8, 3, '2012-07-17 14:30:00', 1),\n(236, 8, 0, '2012-07-17 15:00:00', 1),\n(237, 8, 3, '2012-07-17 15:30:00', 1),\n(238, 8, 3, '2012-07-17 18:00:00', 1),\n(239, 8, 3, '2012-07-17 20:00:00', 1),\n(240, 0, 5, '2012-07-18 13:00:00', 3),\n(241, 0, 5, '2012-07-18 17:30:00', 3),\n(242, 1, 0, '2012-07-18 14:00:00', 3),\n(243, 1, 0, '2012-07-18 16:30:00', 3),\n(244, 2, 1, '2012-07-18 14:00:00', 3),\n(245, 3, 2, '2012-07-18 11:30:00', 2),\n(246, 3, 3, '2012-07-18 19:00:00', 2),\n(247, 4, 1, '2012-07-18 08:30:00', 2),\n(248, 4, 4, '2012-07-18 10:00:00', 2),\n(249, 4, 5, '2012-07-18 19:00:00', 2),\n(250, 5, 0, '2012-07-18 14:30:00', 2),\n(251, 6, 0, '2012-07-18 10:30:00', 2),\n(252, 6, 0, '2012-07-18 13:00:00', 2),\n(253, 6, 0, '2012-07-18 15:00:00', 2),\n(254, 6, 1, '2012-07-18 19:30:00', 2),\n(255, 7, 4, '2012-07-18 08:30:00', 2),\n(256, 7, 4, '2012-07-18 11:00:00', 2),\n(257, 8, 3, '2012-07-18 11:00:00', 1),\n(258, 8, 0, '2012-07-18 13:00:00', 1),\n(259, 8, 3, '2012-07-18 14:30:00', 1),\n(260, 8, 4, '2012-07-18 16:00:00', 1),\n(261, 8, 3, '2012-07-18 16:30:00', 1),\n(262, 8, 4, '2012-07-18 20:00:00', 1),\n(263, 0, 2, '2012-07-19 08:30:00', 3),\n(264, 0, 4, '2012-07-19 10:30:00', 3),\n(265, 0, 5, '2012-07-19 12:00:00', 3),\n(266, 0, 0, '2012-07-19 13:30:00', 3),\n(267, 0, 5, '2012-07-19 16:30:00', 3),\n(268, 1, 1, '2012-07-19 11:30:00', 3),\n(269, 1, 0, '2012-07-19 15:00:00', 3),\n(270, 1, 0, '2012-07-19 18:30:00', 3),\n(271, 2, 1, '2012-07-19 09:30:00', 3),\n(272, 2, 0, '2012-07-19 11:30:00', 3),\n(273, 2, 1, '2012-07-19 14:30:00', 3),\n(274, 2, 2, '2012-07-19 16:00:00', 3),\n(275, 3, 3, '2012-07-19 08:30:00', 2),\n(276, 3, 3, '2012-07-19 17:00:00', 2),\n(277, 3, 3, '2012-07-19 18:30:00', 2),\n(278, 4, 3, '2012-07-19 12:00:00', 2),\n(279, 4, 5, '2012-07-19 14:30:00', 2),\n(280, 4, 0, '2012-07-19 16:30:00', 2),\n(281, 4, 1, '2012-07-19 18:30:00', 2),\n(282, 4, 0, '2012-07-19 19:30:00', 2),\n(283, 5, 0, '2012-07-19 08:30:00', 2),\n(284, 6, 4, '2012-07-19 12:30:00', 2),\n(285, 6, 2, '2012-07-19 14:00:00', 2),\n(286, 6, 0, '2012-07-19 15:00:00', 2),\n(287, 6, 0, '2012-07-19 16:30:00', 2),\n(288, 7, 2, '2012-07-19 13:00:00', 2),\n(289, 7, 0, '2012-07-19 14:00:00', 2),\n(290, 7, 0, '2012-07-19 16:30:00', 2),\n(291, 7, 4, '2012-07-19 17:30:00', 4),\n(292, 8, 3, '2012-07-19 11:00:00', 1),\n(293, 8, 1, '2012-07-19 13:30:00', 1),\n(294, 8, 3, '2012-07-19 14:30:00', 1),\n(295, 8, 3, '2012-07-19 18:00:00', 1),\n(296, 8, 3, '2012-07-19 20:00:00', 1),\n(297, 0, 3, '2012-07-20 08:00:00', 3),\n(298, 0, 5, '2012-07-20 12:00:00', 3),\n(299, 0, 5, '2012-07-20 14:00:00', 3),\n(300, 0, 5, '2012-07-20 17:30:00', 3),\n(301, 0, 0, '2012-07-20 19:00:00', 3),\n(302, 1, 2, '2012-07-20 08:30:00', 3),\n(303, 1, 3, '2012-07-20 12:00:00', 3),\n(304, 1, 4, '2012-07-20 13:30:00', 3),\n(305, 2, 1, '2012-07-20 14:30:00', 3),\n(306, 3, 3, '2012-07-20 15:00:00', 2),\n(307, 3, 1, '2012-07-20 17:30:00', 2),\n(308, 4, 5, '2012-07-20 08:00:00', 2),\n(309, 4, 0, '2012-07-20 13:00:00', 2),\n(310, 4, 1, '2012-07-20 16:30:00', 2),\n(311, 4, 0, '2012-07-20 17:30:00', 2),\n(312, 4, 3, '2012-07-20 18:30:00', 2),\n(313, 6, 0, '2012-07-20 11:00:00', 2),\n(314, 6, 4, '2012-07-20 12:30:00', 2),\n(315, 6, 2, '2012-07-20 15:00:00', 2),\n(316, 6, 0, '2012-07-20 16:00:00', 4),\n(317, 7, 2, '2012-07-20 12:30:00', 2),\n(318, 7, 2, '2012-07-20 16:00:00', 2),\n(319, 7, 4, '2012-07-20 19:30:00', 2),\n(320, 8, 1, '2012-07-20 09:00:00', 1),\n(321, 8, 2, '2012-07-20 12:00:00', 1),\n(322, 8, 3, '2012-07-20 19:30:00', 1),\n(323, 0, 0, '2012-07-21 08:00:00', 3),\n(324, 0, 5, '2012-07-21 11:00:00', 3),\n(325, 0, 5, '2012-07-21 13:30:00', 3),\n(326, 0, 4, '2012-07-21 15:30:00', 3),\n(327, 1, 1, '2012-07-21 09:30:00', 3),\n(328, 1, 0, '2012-07-21 11:00:00', 3),\n(329, 2, 0, '2012-07-21 10:30:00', 3),\n(330, 2, 1, '2012-07-21 13:30:00', 3),\n(331, 3, 2, '2012-07-21 08:00:00', 2),\n(332, 4, 0, '2012-07-21 09:00:00', 2),\n(333, 4, 3, '2012-07-21 10:30:00', 2),\n(334, 4, 0, '2012-07-21 14:00:00', 4),\n(335, 4, 3, '2012-07-21 16:00:00', 2),\n(336, 4, 1, '2012-07-21 17:00:00', 2),\n(337, 4, 0, '2012-07-21 19:00:00', 2),\n(338, 6, 4, '2012-07-21 08:00:00', 2),\n(339, 6, 0, '2012-07-21 09:30:00', 2),\n(340, 6, 0, '2012-07-21 12:00:00', 2),\n(341, 8, 3, '2012-07-21 09:30:00', 1),\n(342, 8, 3, '2012-07-21 11:30:00', 1),\n(343, 8, 3, '2012-07-21 18:00:00', 2),\n(344, 8, 3, '2012-07-21 19:30:00', 1),\n(345, 0, 5, '2012-07-22 10:00:00', 3),\n(346, 0, 0, '2012-07-22 16:00:00', 3),\n(347, 0, 2, '2012-07-22 18:00:00', 3),\n(348, 1, 0, '2012-07-22 08:30:00', 3),\n(349, 1, 0, '2012-07-22 10:30:00', 3),\n(350, 1, 5, '2012-07-22 18:30:00', 3),\n(351, 2, 1, '2012-07-22 08:30:00', 3),\n(352, 2, 1, '2012-07-22 13:30:00', 3),\n(353, 2, 1, '2012-07-22 16:30:00', 3),\n(354, 3, 3, '2012-07-22 11:30:00', 2),\n(355, 3, 2, '2012-07-22 14:00:00', 2),\n(356, 4, 4, '2012-07-22 08:00:00', 2),\n(357, 4, 3, '2012-07-22 10:30:00', 2),\n(358, 4, 0, '2012-07-22 12:00:00', 2),\n(359, 4, 5, '2012-07-22 13:00:00', 2),\n(360, 4, 0, '2012-07-22 16:30:00', 2),\n(361, 4, 1, '2012-07-22 18:00:00', 2),\n(362, 4, 3, '2012-07-22 19:30:00', 2),\n(363, 6, 4, '2012-07-22 10:30:00', 4),\n(364, 6, 0, '2012-07-22 14:30:00', 2),\n(365, 6, 0, '2012-07-22 16:30:00', 2),\n(366, 7, 2, '2012-07-22 10:30:00', 2),\n(367, 7, 2, '2012-07-22 12:00:00', 2),\n(368, 8, 3, '2012-07-22 16:00:00', 1),\n(369, 8, 3, '2012-07-22 17:00:00', 1),\n(370, 8, 2, '2012-07-22 17:30:00', 1),\n(371, 0, 0, '2012-07-23 09:30:00', 3),\n(372, 0, 0, '2012-07-23 12:00:00', 3),\n(373, 0, 5, '2012-07-23 17:00:00', 3),\n(374, 1, 1, '2012-07-23 10:00:00', 3),\n(375, 1, 4, '2012-07-23 12:30:00', 3),\n(376, 1, 4, '2012-07-23 15:30:00', 3),\n(377, 1, 0, '2012-07-23 17:00:00', 3),\n(378, 1, 4, '2012-07-23 19:00:00', 3),\n(379, 2, 1, '2012-07-23 08:00:00', 3),\n(380, 2, 5, '2012-07-23 11:30:00', 3),\n(381, 2, 1, '2012-07-23 13:00:00', 3),\n(382, 2, 1, '2012-07-23 15:00:00', 3),\n(383, 3, 2, '2012-07-23 09:30:00', 2),\n(384, 3, 2, '2012-07-23 19:00:00', 2),\n(385, 4, 4, '2012-07-23 10:00:00', 2),\n(386, 4, 0, '2012-07-23 16:30:00', 2),\n(387, 4, 3, '2012-07-23 19:00:00', 2),\n(388, 5, 3, '2012-07-23 13:00:00', 2),\n(389, 6, 0, '2012-07-23 13:30:00', 2),\n(390, 6, 0, '2012-07-23 15:00:00', 4),\n(391, 6, 0, '2012-07-23 19:00:00', 2),\n(392, 7, 5, '2012-07-23 16:00:00', 2),\n(393, 7, 4, '2012-07-23 18:00:00', 2),\n(394, 8, 3, '2012-07-23 08:30:00', 3),\n(395, 8, 3, '2012-07-23 11:00:00', 1),\n(396, 8, 3, '2012-07-23 14:00:00', 2),\n(397, 8, 2, '2012-07-23 15:00:00', 1),\n(398, 0, 0, '2012-07-24 11:00:00', 3),\n(399, 0, 4, '2012-07-24 13:00:00', 3),\n(400, 0, 5, '2012-07-24 14:30:00', 3),\n(401, 1, 4, '2012-07-24 11:00:00', 3),\n(402, 1, 0, '2012-07-24 16:00:00', 6),\n(403, 1, 1, '2012-07-24 19:00:00', 3),\n(404, 2, 1, '2012-07-24 09:00:00', 3),\n(405, 2, 2, '2012-07-24 12:30:00', 3),\n(406, 3, 3, '2012-07-24 09:00:00', 2),\n(407, 3, 3, '2012-07-24 17:30:00', 2),\n(408, 4, 0, '2012-07-24 08:30:00', 2),\n(409, 4, 5, '2012-07-24 09:30:00', 2),\n(410, 4, 0, '2012-07-24 11:30:00', 2),\n(411, 4, 1, '2012-07-24 14:30:00', 2),\n(412, 4, 0, '2012-07-24 15:30:00', 2),\n(413, 4, 0, '2012-07-24 17:30:00', 2),\n(414, 4, 0, '2012-07-24 19:30:00', 2),\n(415, 5, 5, '2012-07-24 16:30:00', 2),\n(416, 6, 0, '2012-07-24 09:30:00', 2),\n(417, 6, 0, '2012-07-24 14:30:00', 2),\n(418, 7, 4, '2012-07-24 09:30:00', 2),\n(419, 7, 5, '2012-07-24 11:30:00', 2),\n(420, 7, 2, '2012-07-24 16:30:00', 2),\n(421, 7, 4, '2012-07-24 18:00:00', 2),\n(422, 7, 2, '2012-07-24 19:30:00', 2),\n(423, 8, 3, '2012-07-24 08:30:00', 1),\n(424, 8, 3, '2012-07-24 10:30:00', 2),\n(425, 8, 3, '2012-07-24 12:00:00', 1),\n(426, 8, 3, '2012-07-24 14:00:00', 1),\n(427, 8, 0, '2012-07-24 15:00:00', 1),\n(428, 8, 4, '2012-07-24 16:30:00', 1),\n(429, 8, 0, '2012-07-24 20:00:00', 1),\n(430, 0, 5, '2012-07-25 08:00:00', 3),\n(431, 0, 0, '2012-07-25 12:30:00', 3),\n(432, 0, 0, '2012-07-25 16:30:00', 3),\n(433, 1, 1, '2012-07-25 08:00:00', 3),\n(434, 1, 0, '2012-07-25 10:30:00', 3),\n(435, 1, 4, '2012-07-25 15:00:00', 3),\n(436, 2, 1, '2012-07-25 13:30:00', 3),\n(437, 2, 1, '2012-07-25 17:30:00', 3),\n(438, 3, 2, '2012-07-25 10:00:00', 2),\n(439, 3, 3, '2012-07-25 14:00:00', 4),\n(440, 3, 3, '2012-07-25 17:00:00', 2),\n(441, 3, 2, '2012-07-25 18:30:00', 2),\n(442, 4, 3, '2012-07-25 08:30:00', 2),\n(443, 4, 0, '2012-07-25 09:30:00', 4),\n(444, 4, 3, '2012-07-25 11:30:00', 4),\n(445, 4, 5, '2012-07-25 13:30:00', 4),\n(446, 4, 3, '2012-07-25 16:00:00', 2),\n(447, 4, 3, '2012-07-25 18:00:00', 2),\n(448, 4, 3, '2012-07-25 19:30:00', 2),\n(449, 5, 0, '2012-07-25 18:30:00', 2),\n(450, 6, 4, '2012-07-25 08:30:00', 2),\n(451, 6, 1, '2012-07-25 09:30:00', 2),\n(452, 6, 0, '2012-07-25 12:00:00', 2),\n(453, 6, 0, '2012-07-25 13:30:00', 2),\n(454, 6, 0, '2012-07-25 16:30:00', 4),\n(455, 6, 5, '2012-07-25 19:00:00', 2),\n(456, 7, 5, '2012-07-25 10:30:00', 2),\n(457, 7, 2, '2012-07-25 14:00:00', 2),\n(458, 7, 2, '2012-07-25 16:00:00', 2),\n(459, 8, 3, '2012-07-25 08:00:00', 1),\n(460, 8, 3, '2012-07-25 10:00:00', 1),\n(461, 8, 4, '2012-07-25 14:30:00', 1),\n(462, 8, 1, '2012-07-25 16:00:00', 1),\n(463, 8, 2, '2012-07-25 20:00:00', 1),\n(464, 0, 4, '2012-07-26 09:00:00', 3),\n(465, 0, 0, '2012-07-26 11:30:00', 3),\n(466, 0, 4, '2012-07-26 18:00:00', 3),\n(467, 1, 8, '2012-07-26 08:00:00', 3),\n(468, 1, 8, '2012-07-26 11:30:00', 3),\n(469, 1, 8, '2012-07-26 13:30:00', 3),\n(470, 1, 1, '2012-07-26 15:00:00', 3),\n(471, 1, 0, '2012-07-26 16:30:00', 3),\n(472, 1, 6, '2012-07-26 19:00:00', 3),\n(473, 2, 1, '2012-07-26 08:30:00', 3),\n(474, 2, 2, '2012-07-26 11:00:00', 6),\n(475, 2, 7, '2012-07-26 14:00:00', 3),\n(476, 2, 2, '2012-07-26 17:00:00', 3),\n(477, 2, 3, '2012-07-26 19:00:00', 3),\n(478, 3, 0, '2012-07-26 09:00:00', 2),\n(479, 3, 0, '2012-07-26 13:30:00', 2),\n(480, 3, 3, '2012-07-26 16:00:00', 2),\n(481, 4, 3, '2012-07-26 08:00:00', 2),\n(482, 4, 6, '2012-07-26 09:00:00', 2),\n(483, 4, 0, '2012-07-26 12:00:00', 2),\n(484, 4, 5, '2012-07-26 13:30:00', 2),\n(485, 4, 6, '2012-07-26 16:00:00', 2),\n(486, 4, 7, '2012-07-26 17:30:00', 2),\n(487, 6, 0, '2012-07-26 10:00:00', 4),\n(488, 6, 0, '2012-07-26 13:00:00', 2),\n(489, 6, 0, '2012-07-26 19:00:00', 2),\n(490, 7, 7, '2012-07-26 09:30:00', 2),\n(491, 7, 6, '2012-07-26 11:00:00', 2),\n(492, 7, 5, '2012-07-26 12:30:00', 2),\n(493, 7, 4, '2012-07-26 13:30:00', 2),\n(494, 7, 5, '2012-07-26 17:00:00', 2),\n(495, 8, 3, '2012-07-26 12:00:00', 1),\n(496, 8, 3, '2012-07-26 13:30:00', 1),\n(497, 8, 2, '2012-07-26 15:00:00', 1),\n(498, 8, 1, '2012-07-26 16:30:00', 1),\n(499, 8, 3, '2012-07-26 17:00:00', 1),\n(500, 0, 4, '2012-07-27 08:00:00', 3),\n(501, 0, 5, '2012-07-27 11:00:00', 3),\n(502, 0, 6, '2012-07-27 14:00:00', 3),\n(503, 0, 6, '2012-07-27 17:30:00', 3),\n(504, 1, 0, '2012-07-27 10:00:00', 3),\n(505, 1, 7, '2012-07-27 11:30:00', 3),\n(506, 1, 0, '2012-07-27 13:00:00', 3),\n(507, 1, 0, '2012-07-27 15:00:00', 3),\n(508, 1, 0, '2012-07-27 18:00:00', 3),\n(509, 2, 1, '2012-07-27 12:00:00', 6),\n(510, 3, 0, '2012-07-27 10:30:00', 2),\n(511, 3, 2, '2012-07-27 14:00:00', 2),\n(512, 3, 3, '2012-07-27 19:00:00', 2),\n(513, 4, 1, '2012-07-27 10:00:00', 4),\n(514, 4, 6, '2012-07-27 12:30:00', 2),\n(515, 4, 0, '2012-07-27 14:00:00', 4),\n(516, 4, 0, '2012-07-27 16:30:00', 2),\n(517, 4, 1, '2012-07-27 17:30:00', 2),\n(518, 4, 0, '2012-07-27 18:30:00', 2),\n(519, 5, 7, '2012-07-27 18:00:00', 2),\n(520, 6, 0, '2012-07-27 09:00:00', 2),\n(521, 6, 5, '2012-07-27 14:00:00', 2),\n(522, 6, 8, '2012-07-27 16:30:00', 2),\n(523, 7, 2, '2012-07-27 18:00:00', 2),\n(524, 7, 4, '2012-07-27 19:00:00', 2),\n(525, 8, 3, '2012-07-27 09:00:00', 1),\n(526, 8, 3, '2012-07-27 12:30:00', 1),\n(527, 8, 3, '2012-07-27 16:00:00', 1),\n(528, 8, 6, '2012-07-27 16:30:00', 1),\n(529, 8, 0, '2012-07-27 18:30:00', 1),\n(530, 0, 7, '2012-07-28 08:00:00', 9),\n(531, 0, 4, '2012-07-28 13:00:00', 3),\n(532, 0, 5, '2012-07-28 15:00:00', 3),\n(533, 0, 2, '2012-07-28 19:00:00', 3),\n(534, 1, 1, '2012-07-28 08:00:00', 3),\n(535, 1, 0, '2012-07-28 10:00:00', 3),\n(536, 1, 0, '2012-07-28 16:00:00', 3),\n(537, 1, 7, '2012-07-28 17:30:00', 3),\n(538, 2, 1, '2012-07-28 10:00:00', 3),\n(539, 2, 1, '2012-07-28 14:00:00', 3),\n(540, 2, 1, '2012-07-28 17:00:00', 3),\n(541, 2, 5, '2012-07-28 18:30:00', 3),\n(542, 3, 3, '2012-07-28 08:30:00', 2),\n(543, 3, 3, '2012-07-28 15:30:00', 2),\n(544, 4, 0, '2012-07-28 09:00:00', 2),\n(545, 4, 3, '2012-07-28 10:30:00', 4),\n(546, 4, 0, '2012-07-28 12:30:00', 2),\n(547, 4, 8, '2012-07-28 16:00:00', 2),\n(548, 4, 0, '2012-07-28 19:00:00', 2),\n(549, 5, 0, '2012-07-28 18:00:00', 2),\n(550, 6, 0, '2012-07-28 17:00:00', 2),\n(551, 6, 0, '2012-07-28 18:30:00', 2),\n(552, 7, 2, '2012-07-28 09:00:00', 2),\n(553, 7, 5, '2012-07-28 10:00:00', 2),\n(554, 7, 6, '2012-07-28 12:30:00', 2),\n(555, 7, 8, '2012-07-28 17:00:00', 4),\n(556, 8, 2, '2012-07-28 16:00:00', 1),\n(557, 8, 3, '2012-07-28 16:30:00', 1),\n(558, 8, 4, '2012-07-28 19:00:00', 1),\n(559, 0, 7, '2012-07-29 09:30:00', 3),\n(560, 0, 2, '2012-07-29 11:00:00', 3),\n(561, 0, 6, '2012-07-29 13:00:00', 3),\n(562, 0, 5, '2012-07-29 15:00:00', 3),\n(563, 0, 0, '2012-07-29 17:00:00', 3),\n(564, 1, 8, '2012-07-29 09:30:00', 3),\n(565, 1, 0, '2012-07-29 15:00:00', 3),\n(566, 1, 8, '2012-07-29 16:30:00', 3),\n(567, 2, 1, '2012-07-29 08:30:00', 3),\n(568, 2, 1, '2012-07-29 12:00:00', 6),\n(569, 2, 1, '2012-07-29 15:30:00', 3),\n(570, 4, 3, '2012-07-29 08:00:00', 2),\n(571, 4, 0, '2012-07-29 09:00:00', 2),\n(572, 4, 3, '2012-07-29 10:30:00', 2),\n(573, 4, 8, '2012-07-29 11:30:00', 4),\n(574, 4, 8, '2012-07-29 15:00:00', 2),\n(575, 4, 0, '2012-07-29 18:30:00', 2),\n(576, 6, 0, '2012-07-29 09:00:00', 2),\n(577, 6, 0, '2012-07-29 10:30:00', 2),\n(578, 6, 6, '2012-07-29 17:30:00', 4),\n(579, 7, 4, '2012-07-29 16:00:00', 2),\n(580, 7, 8, '2012-07-29 18:30:00', 2),\n(581, 8, 3, '2012-07-29 12:30:00', 1),\n(582, 8, 7, '2012-07-29 13:00:00', 1),\n(583, 8, 3, '2012-07-29 15:30:00', 1),\n(584, 8, 3, '2012-07-29 18:00:00', 1),\n(585, 0, 5, '2012-07-30 14:00:00', 3),\n(586, 0, 6, '2012-07-30 15:30:00', 3),\n(587, 0, 7, '2012-07-30 19:00:00', 3),\n(588, 1, 8, '2012-07-30 08:30:00', 3),\n(589, 1, 7, '2012-07-30 11:00:00', 3),\n(590, 1, 2, '2012-07-30 13:30:00', 3),\n(591, 1, 1, '2012-07-30 15:30:00', 3),\n(592, 2, 5, '2012-07-30 10:00:00', 3),\n(593, 2, 8, '2012-07-30 11:30:00', 3),\n(594, 2, 7, '2012-07-30 15:00:00', 3),\n(595, 2, 0, '2012-07-30 17:30:00', 3),\n(596, 3, 3, '2012-07-30 11:30:00', 2),\n(597, 3, 4, '2012-07-30 16:30:00', 2),\n(598, 4, 0, '2012-07-30 08:00:00', 2),\n(599, 4, 0, '2012-07-30 10:30:00', 2),\n(600, 4, 0, '2012-07-30 12:00:00', 2),\n(601, 4, 7, '2012-07-30 18:00:00', 2),\n(602, 4, 3, '2012-07-30 19:30:00', 2),\n(603, 5, 0, '2012-07-30 12:30:00', 2),\n(604, 5, 0, '2012-07-30 14:00:00', 2),\n(605, 6, 0, '2012-07-30 08:30:00', 2),\n(606, 6, 0, '2012-07-30 12:00:00', 2),\n(607, 6, 0, '2012-07-30 14:30:00', 2),\n(608, 6, 0, '2012-07-30 17:30:00', 2),\n(609, 7, 7, '2012-07-30 08:00:00', 2),\n(610, 7, 6, '2012-07-30 09:30:00', 2),\n(611, 7, 8, '2012-07-30 14:30:00', 2),\n(612, 7, 5, '2012-07-30 16:30:00', 2),\n(613, 7, 4, '2012-07-30 18:00:00', 2),\n(614, 7, 6, '2012-07-30 19:00:00', 2),\n(615, 8, 3, '2012-07-30 08:30:00', 1),\n(616, 8, 2, '2012-07-30 09:00:00', 1),\n(617, 8, 2, '2012-07-30 11:00:00', 1),\n(618, 8, 2, '2012-07-30 12:30:00', 1),\n(619, 8, 3, '2012-07-30 15:00:00', 2),\n(620, 8, 5, '2012-07-30 16:00:00', 1),\n(621, 8, 2, '2012-07-30 16:30:00', 1),\n(622, 8, 3, '2012-07-30 18:30:00', 1),\n(623, 8, 1, '2012-07-30 19:30:00', 1),\n(624, 0, 7, '2012-07-31 09:30:00', 3),\n(625, 0, 0, '2012-07-31 11:00:00', 3),\n(626, 0, 0, '2012-07-31 15:00:00', 3),\n(627, 0, 5, '2012-07-31 17:00:00', 3),\n(628, 0, 0, '2012-07-31 18:30:00', 3),\n(629, 1, 0, '2012-07-31 08:00:00', 3),\n(630, 1, 7, '2012-07-31 13:00:00', 3),\n(631, 2, 1, '2012-07-31 16:30:00', 3),\n(632, 3, 3, '2012-07-31 08:30:00', 2),\n(633, 3, 1, '2012-07-31 13:00:00', 2),\n(634, 3, 1, '2012-07-31 15:30:00', 2),\n(635, 4, 8, '2012-07-31 09:30:00', 2),\n(636, 4, 0, '2012-07-31 11:00:00', 2),\n(637, 4, 3, '2012-07-31 12:00:00', 2),\n(638, 4, 2, '2012-07-31 13:00:00', 2),\n(639, 4, 3, '2012-07-31 14:00:00', 2),\n(640, 4, 0, '2012-07-31 15:00:00', 2),\n(641, 4, 6, '2012-07-31 17:00:00', 2),\n(642, 4, 7, '2012-07-31 18:30:00', 2),\n(643, 4, 0, '2012-07-31 19:30:00', 2),\n(644, 6, 0, '2012-07-31 09:00:00', 2),\n(645, 6, 5, '2012-07-31 10:00:00', 2),\n(646, 6, 6, '2012-07-31 11:00:00', 2),\n(647, 6, 0, '2012-07-31 14:30:00', 2),\n(648, 6, 6, '2012-07-31 16:00:00', 2),\n(649, 7, 4, '2012-07-31 18:30:00', 2),\n(650, 8, 3, '2012-07-31 10:00:00', 1),\n(651, 8, 3, '2012-07-31 11:30:00', 1),\n(652, 8, 5, '2012-07-31 12:00:00', 1),\n(653, 8, 7, '2012-07-31 12:30:00', 1),\n(654, 8, 8, '2012-07-31 13:30:00', 1),\n(655, 8, 6, '2012-07-31 14:00:00', 1),\n(656, 8, 4, '2012-07-31 17:00:00', 1),\n(657, 8, 2, '2012-07-31 17:30:00', 1),\n(658, 0, 5, '2012-08-01 15:30:00', 3),\n(659, 0, 5, '2012-08-01 18:00:00', 3),\n(660, 1, 8, '2012-08-01 09:00:00', 9),\n(661, 1, 8, '2012-08-01 17:30:00', 3),\n(662, 2, 1, '2012-08-01 09:30:00', 6),\n(663, 2, 1, '2012-08-01 14:30:00', 3),\n(664, 2, 1, '2012-08-01 16:30:00', 3),\n(665, 3, 7, '2012-08-01 13:00:00', 2),\n(666, 4, 5, '2012-08-01 08:00:00', 2),\n(667, 4, 6, '2012-08-01 09:00:00', 2),\n(668, 4, 0, '2012-08-01 10:30:00', 6),\n(669, 4, 3, '2012-08-01 13:30:00', 4),\n(670, 4, 3, '2012-08-01 19:30:00', 2),\n(671, 5, 7, '2012-08-01 08:30:00', 2),\n(672, 5, 0, '2012-08-01 14:30:00', 2),\n(673, 6, 0, '2012-08-01 09:30:00', 2),\n(674, 6, 0, '2012-08-01 11:00:00', 4),\n(675, 6, 6, '2012-08-01 14:30:00', 2),\n(676, 6, 0, '2012-08-01 18:00:00', 2),\n(677, 7, 4, '2012-08-01 12:30:00', 2),\n(678, 7, 2, '2012-08-01 16:00:00', 2),\n(679, 7, 5, '2012-08-01 17:00:00', 2),\n(680, 8, 3, '2012-08-01 08:30:00', 2),\n(681, 8, 2, '2012-08-01 09:30:00', 1),\n(682, 8, 3, '2012-08-01 10:30:00', 1),\n(683, 8, 3, '2012-08-01 11:30:00', 1),\n(684, 8, 8, '2012-08-01 13:30:00', 1),\n(685, 8, 8, '2012-08-01 15:00:00', 1),\n(686, 8, 3, '2012-08-01 17:00:00', 1),\n(687, 0, 8, '2012-08-02 08:00:00', 3),\n(688, 0, 5, '2012-08-02 13:00:00', 3),\n(689, 0, 7, '2012-08-02 15:30:00', 3),\n(690, 0, 5, '2012-08-02 18:30:00', 3),\n(691, 1, 8, '2012-08-02 09:30:00', 3),\n(692, 1, 8, '2012-08-02 12:00:00', 3),\n(693, 1, 0, '2012-08-02 13:30:00', 3),\n(694, 1, 5, '2012-08-02 15:30:00', 3),\n(695, 1, 0, '2012-08-02 18:00:00', 3),\n(696, 2, 1, '2012-08-02 09:30:00', 3),\n(697, 2, 0, '2012-08-02 11:30:00', 3),\n(698, 2, 3, '2012-08-02 14:00:00', 3),\n(699, 2, 1, '2012-08-02 19:00:00', 3),\n(700, 3, 3, '2012-08-02 10:00:00', 2),\n(701, 3, 2, '2012-08-02 15:00:00', 2),\n(702, 3, 3, '2012-08-02 17:00:00', 2),\n(703, 3, 6, '2012-08-02 18:00:00', 2),\n(704, 3, 4, '2012-08-02 19:30:00', 2),\n(705, 4, 4, '2012-08-02 10:00:00', 2),\n(706, 4, 7, '2012-08-02 11:30:00', 2),\n(707, 4, 5, '2012-08-02 14:30:00', 2),\n(708, 4, 8, '2012-08-02 15:30:00', 2),\n(709, 4, 8, '2012-08-02 17:00:00', 2),\n(710, 4, 3, '2012-08-02 18:30:00', 2),\n(711, 4, 0, '2012-08-02 19:30:00', 2),\n(712, 6, 4, '2012-08-02 09:00:00', 2),\n(713, 6, 0, '2012-08-02 10:00:00', 2),\n(714, 6, 0, '2012-08-02 11:30:00', 2),\n(715, 6, 6, '2012-08-02 12:30:00', 2),\n(716, 6, 8, '2012-08-02 14:00:00', 2),\n(717, 6, 0, '2012-08-02 17:00:00', 4),\n(718, 6, 2, '2012-08-02 19:30:00', 2),\n(719, 7, 7, '2012-08-02 08:00:00', 2),\n(720, 7, 5, '2012-08-02 11:00:00', 2),\n(721, 7, 6, '2012-08-02 14:00:00', 2),\n(722, 7, 4, '2012-08-02 16:00:00', 2),\n(723, 7, 0, '2012-08-02 18:00:00', 2),\n(724, 8, 3, '2012-08-02 08:30:00', 1),\n(725, 8, 3, '2012-08-02 13:00:00', 1),\n(726, 8, 7, '2012-08-02 15:00:00', 1),\n(727, 8, 3, '2012-08-02 16:30:00', 1),\n(728, 8, 7, '2012-08-02 17:00:00', 1),\n(729, 8, 3, '2012-08-02 19:30:00', 1),\n(730, 0, 5, '2012-08-03 11:30:00', 3),\n(731, 0, 0, '2012-08-03 16:00:00', 3),\n(732, 0, 6, '2012-08-03 18:30:00', 3),\n(733, 1, 8, '2012-08-03 10:30:00', 3),\n(734, 1, 0, '2012-08-03 13:00:00', 6),\n(735, 1, 7, '2012-08-03 16:30:00', 3),\n(736, 1, 8, '2012-08-03 19:00:00', 3),\n(737, 2, 8, '2012-08-03 08:30:00', 3),\n(738, 2, 1, '2012-08-03 11:00:00', 3),\n(739, 3, 6, '2012-08-03 08:00:00', 2),\n(740, 3, 2, '2012-08-03 10:00:00', 2),\n(741, 3, 6, '2012-08-03 12:00:00', 2),\n(742, 3, 6, '2012-08-03 16:30:00', 2),\n(743, 3, 2, '2012-08-03 18:30:00', 2),\n(744, 4, 0, '2012-08-03 09:30:00', 2),\n(745, 4, 7, '2012-08-03 10:30:00', 2),\n(746, 4, 0, '2012-08-03 11:30:00', 2),\n(747, 4, 0, '2012-08-03 13:00:00', 2),\n(748, 4, 1, '2012-08-03 14:30:00', 2),\n(749, 4, 3, '2012-08-03 15:30:00', 4),\n(750, 4, 3, '2012-08-03 18:30:00', 2),\n(751, 6, 0, '2012-08-03 09:00:00', 2),\n(752, 6, 0, '2012-08-03 10:30:00', 2),\n(753, 6, 4, '2012-08-03 12:00:00', 2),\n(754, 6, 0, '2012-08-03 15:30:00', 2),\n(755, 6, 1, '2012-08-03 16:30:00', 2),\n(756, 6, 0, '2012-08-03 19:00:00', 2),\n(757, 7, 6, '2012-08-03 09:00:00', 2),\n(758, 7, 6, '2012-08-03 10:30:00', 2),\n(759, 7, 2, '2012-08-03 13:30:00', 2),\n(760, 7, 5, '2012-08-03 17:30:00', 2),\n(761, 8, 8, '2012-08-03 12:00:00', 1),\n(762, 8, 3, '2012-08-03 12:30:00', 1),\n(763, 8, 6, '2012-08-03 14:00:00', 1),\n(764, 8, 3, '2012-08-03 15:00:00', 1),\n(765, 8, 6, '2012-08-03 15:30:00', 1),\n(766, 8, 8, '2012-08-03 16:00:00', 1),\n(767, 8, 0, '2012-08-03 19:00:00', 1),\n(768, 8, 3, '2012-08-03 19:30:00', 1),\n(769, 0, 6, '2012-08-04 15:00:00', 3),\n(770, 1, 9, '2012-08-04 09:30:00', 3),\n(771, 1, 0, '2012-08-04 11:30:00', 3),\n(772, 1, 8, '2012-08-04 16:00:00', 3),\n(773, 1, 0, '2012-08-04 18:30:00', 3),\n(774, 2, 1, '2012-08-04 08:00:00', 3),\n(775, 2, 2, '2012-08-04 09:30:00', 3),\n(776, 2, 1, '2012-08-04 11:00:00', 3),\n(777, 2, 2, '2012-08-04 16:30:00', 3),\n(778, 2, 9, '2012-08-04 18:30:00', 3),\n(779, 3, 6, '2012-08-04 11:30:00', 2),\n(780, 3, 1, '2012-08-04 15:00:00', 2),\n(781, 3, 3, '2012-08-04 18:00:00', 2),\n(782, 3, 4, '2012-08-04 19:00:00', 2),\n(783, 4, 8, '2012-08-04 08:30:00', 2),\n(784, 4, 7, '2012-08-04 10:00:00', 2),\n(785, 4, 0, '2012-08-04 13:30:00', 2),\n(786, 4, 5, '2012-08-04 14:30:00', 2),\n(787, 4, 0, '2012-08-04 17:00:00', 2),\n(788, 4, 5, '2012-08-04 19:30:00', 2),\n(789, 5, 0, '2012-08-04 12:30:00', 2),\n(790, 6, 6, '2012-08-04 08:30:00', 2),\n(791, 6, 5, '2012-08-04 09:30:00', 2),\n(792, 6, 6, '2012-08-04 12:30:00', 2),\n(793, 6, 0, '2012-08-04 16:00:00', 2),\n(794, 6, 0, '2012-08-04 17:30:00', 2),\n(795, 7, 5, '2012-08-04 08:00:00', 2),\n(796, 7, 9, '2012-08-04 11:00:00', 2),\n(797, 7, 7, '2012-08-04 15:00:00', 2),\n(798, 7, 5, '2012-08-04 18:30:00', 2),\n(799, 8, 3, '2012-08-04 08:00:00', 1),\n(800, 8, 3, '2012-08-04 11:00:00', 2),\n(801, 8, 3, '2012-08-04 13:00:00', 1),\n(802, 8, 3, '2012-08-04 16:30:00', 1),\n(803, 8, 6, '2012-08-04 18:00:00', 1),\n(804, 8, 7, '2012-08-04 18:30:00', 1),\n(805, 8, 3, '2012-08-04 19:00:00', 1),\n(806, 0, 2, '2012-08-05 08:00:00', 3),\n(807, 0, 5, '2012-08-05 09:30:00', 3),\n(808, 0, 7, '2012-08-05 15:00:00', 3),\n(809, 0, 7, '2012-08-05 17:30:00', 3),\n(810, 1, 0, '2012-08-05 08:00:00', 3),\n(811, 1, 7, '2012-08-05 09:30:00', 3),\n(812, 1, 9, '2012-08-05 11:00:00', 3),\n(813, 1, 9, '2012-08-05 15:30:00', 3),\n(814, 1, 1, '2012-08-05 18:00:00', 3),\n(815, 2, 1, '2012-08-05 10:00:00', 3),\n(816, 2, 5, '2012-08-05 11:30:00', 3),\n(817, 2, 2, '2012-08-05 15:00:00', 3),\n(818, 2, 8, '2012-08-05 17:00:00', 3),\n(819, 3, 3, '2012-08-05 09:30:00', 2),\n(820, 3, 4, '2012-08-05 14:30:00', 2),\n(821, 3, 3, '2012-08-05 15:30:00', 2),\n(822, 4, 0, '2012-08-05 08:30:00', 2),\n(823, 4, 0, '2012-08-05 10:00:00', 2),\n(824, 4, 0, '2012-08-05 11:30:00', 2),\n(825, 4, 4, '2012-08-05 16:00:00', 2),\n(826, 4, 8, '2012-08-05 19:00:00', 2),\n(827, 6, 0, '2012-08-05 10:00:00', 4),\n(828, 6, 6, '2012-08-05 13:00:00', 2),\n(829, 6, 0, '2012-08-05 15:30:00', 2),\n(830, 7, 2, '2012-08-05 10:30:00', 2),\n(831, 7, 8, '2012-08-05 15:30:00', 2),\n(832, 7, 2, '2012-08-05 19:30:00', 2),\n(833, 8, 0, '2012-08-05 08:30:00', 1),\n(834, 8, 3, '2012-08-05 13:00:00', 1),\n(835, 8, 0, '2012-08-05 14:00:00', 1),\n(836, 8, 3, '2012-08-05 16:30:00', 1),\n(837, 8, 3, '2012-08-05 17:30:00', 1),\n(838, 8, 3, '2012-08-05 19:30:00', 2),\n(839, 0, 7, '2012-08-06 09:00:00', 3),\n(840, 0, 0, '2012-08-06 10:30:00', 3),\n(841, 0, 2, '2012-08-06 12:00:00', 3),\n(842, 0, 0, '2012-08-06 13:30:00', 3),\n(843, 0, 7, '2012-08-06 15:00:00', 3),\n(844, 0, 5, '2012-08-06 16:30:00', 3),\n(845, 0, 7, '2012-08-06 18:00:00', 3),\n(846, 1, 8, '2012-08-06 08:00:00', 3),\n(847, 1, 3, '2012-08-06 10:00:00', 3),\n(848, 1, 0, '2012-08-06 11:30:00', 3),\n(849, 1, 9, '2012-08-06 14:30:00', 3),\n(850, 1, 9, '2012-08-06 17:30:00', 3),\n(851, 2, 1, '2012-08-06 08:30:00', 3),\n(852, 2, 5, '2012-08-06 10:30:00', 3),\n(853, 2, 8, '2012-08-06 12:00:00', 3),\n(854, 2, 8, '2012-08-06 14:00:00', 3),\n(855, 3, 3, '2012-08-06 08:30:00', 2),\n(856, 3, 6, '2012-08-06 15:00:00', 2),\n(857, 3, 6, '2012-08-06 17:00:00', 2),\n(858, 4, 0, '2012-08-06 08:00:00', 4),\n(859, 4, 0, '2012-08-06 12:00:00', 2),\n(860, 4, 7, '2012-08-06 13:30:00', 2),\n(861, 4, 0, '2012-08-06 16:30:00', 2),\n(862, 4, 6, '2012-08-06 18:30:00', 2),\n(863, 5, 0, '2012-08-06 11:00:00', 2),\n(864, 6, 6, '2012-08-06 09:00:00', 2),\n(865, 6, 0, '2012-08-06 10:00:00', 2),\n(866, 6, 6, '2012-08-06 13:00:00', 2),\n(867, 6, 0, '2012-08-06 14:00:00', 2),\n(868, 6, 5, '2012-08-06 15:00:00', 2),\n(869, 7, 8, '2012-08-06 09:30:00', 2),\n(870, 7, 2, '2012-08-06 11:00:00', 2),\n(871, 7, 5, '2012-08-06 12:00:00', 4),\n(872, 7, 4, '2012-08-06 17:30:00', 2),\n(873, 7, 2, '2012-08-06 19:00:00', 2),\n(874, 8, 3, '2012-08-06 08:00:00', 1),\n(875, 8, 4, '2012-08-06 09:00:00', 1),\n(876, 8, 3, '2012-08-06 09:30:00', 1),\n(877, 8, 6, '2012-08-06 12:00:00', 1),\n(878, 8, 1, '2012-08-06 18:00:00', 1),\n(879, 8, 8, '2012-08-06 18:30:00', 1),\n(880, 8, 3, '2012-08-06 19:00:00', 1),\n(881, 0, 10, '2012-08-07 09:00:00', 3),\n(882, 1, 0, '2012-08-07 08:00:00', 3),\n(883, 1, 8, '2012-08-07 09:30:00', 3),\n(884, 1, 7, '2012-08-07 17:00:00', 3),\n(885, 2, 1, '2012-08-07 09:00:00', 6),\n(886, 2, 1, '2012-08-07 13:00:00', 3),\n(887, 2, 10, '2012-08-07 15:00:00', 3),\n(888, 2, 2, '2012-08-07 18:00:00', 3),\n(889, 3, 6, '2012-08-07 08:30:00', 2),\n(890, 3, 6, '2012-08-07 10:00:00', 2),\n(891, 3, 3, '2012-08-07 11:00:00', 2),\n(892, 3, 3, '2012-08-07 12:30:00', 2),\n(893, 3, 3, '2012-08-07 14:30:00', 2),\n(894, 4, 0, '2012-08-07 08:30:00', 2),\n(895, 4, 8, '2012-08-07 12:00:00', 2),\n(896, 4, 8, '2012-08-07 13:30:00', 2),\n(897, 4, 0, '2012-08-07 15:30:00', 2),\n(898, 4, 6, '2012-08-07 18:30:00', 2),\n(899, 6, 1, '2012-08-07 08:00:00', 2),\n(900, 6, 0, '2012-08-07 14:00:00', 2),\n(901, 6, 0, '2012-08-07 15:30:00', 2),\n(902, 6, 0, '2012-08-07 18:00:00', 2),\n(903, 7, 10, '2012-08-07 12:30:00', 2),\n(904, 7, 4, '2012-08-07 15:00:00', 2),\n(905, 7, 9, '2012-08-07 18:30:00', 2),\n(906, 8, 3, '2012-08-07 08:30:00', 2),\n(907, 8, 2, '2012-08-07 10:00:00', 1),\n(908, 8, 3, '2012-08-07 10:30:00', 1),\n(909, 8, 0, '2012-08-07 11:00:00', 1),\n(910, 8, 3, '2012-08-07 12:00:00', 1),\n(911, 8, 2, '2012-08-07 12:30:00', 1),\n(912, 8, 2, '2012-08-07 14:30:00', 1),\n(913, 8, 0, '2012-08-07 16:30:00', 1),\n(914, 8, 3, '2012-08-07 17:00:00', 2),\n(915, 8, 8, '2012-08-07 19:30:00', 1),\n(916, 0, 10, '2012-08-08 09:00:00', 3),\n(917, 0, 6, '2012-08-08 12:30:00', 3),\n(918, 0, 5, '2012-08-08 14:00:00', 3),\n(919, 0, 6, '2012-08-08 16:30:00', 3),\n(920, 1, 0, '2012-08-08 08:30:00', 6),\n(921, 1, 10, '2012-08-08 11:30:00', 3),\n(922, 1, 10, '2012-08-08 14:00:00', 3),\n(923, 1, 9, '2012-08-08 19:00:00', 3),\n(924, 2, 5, '2012-08-08 08:00:00', 3),\n(925, 2, 9, '2012-08-08 09:30:00', 3),\n(926, 2, 7, '2012-08-08 11:00:00', 3),\n(927, 2, 1, '2012-08-08 14:00:00', 3),\n(928, 2, 5, '2012-08-08 17:30:00', 3),\n(929, 2, 1, '2012-08-08 19:00:00', 3),\n(930, 3, 10, '2012-08-08 08:00:00', 2),\n(931, 3, 6, '2012-08-08 10:00:00', 2),\n(932, 3, 0, '2012-08-08 12:00:00', 2),\n(933, 3, 10, '2012-08-08 15:30:00', 2),\n(934, 3, 2, '2012-08-08 19:00:00', 2),\n(935, 4, 6, '2012-08-08 08:00:00', 2),\n(936, 4, 8, '2012-08-08 11:00:00', 2),\n(937, 4, 9, '2012-08-08 12:30:00', 4),\n(938, 4, 0, '2012-08-08 15:00:00', 2),\n(939, 4, 0, '2012-08-08 16:30:00', 2),\n(940, 4, 3, '2012-08-08 17:30:00', 2),\n(941, 5, 0, '2012-08-08 08:00:00', 2),\n(942, 6, 0, '2012-08-08 09:00:00', 2),\n(943, 6, 0, '2012-08-08 11:00:00', 2),\n(944, 6, 8, '2012-08-08 12:30:00', 2),\n(945, 6, 6, '2012-08-08 15:00:00', 2),\n(946, 6, 10, '2012-08-08 17:30:00', 2),\n(947, 6, 0, '2012-08-08 19:00:00', 2),\n(948, 7, 8, '2012-08-08 08:00:00', 2),\n(949, 7, 9, '2012-08-08 11:00:00', 2),\n(950, 7, 7, '2012-08-08 12:30:00', 2),\n(951, 7, 4, '2012-08-08 14:00:00', 2),\n(952, 7, 9, '2012-08-08 15:30:00', 2),\n(953, 7, 10, '2012-08-08 18:30:00', 2),\n(954, 8, 4, '2012-08-08 08:00:00', 1),\n(955, 8, 2, '2012-08-08 09:00:00', 1),\n(956, 8, 3, '2012-08-08 10:00:00', 1),\n(957, 8, 3, '2012-08-08 11:00:00', 1),\n(958, 8, 3, '2012-08-08 12:00:00', 1),\n(959, 8, 2, '2012-08-08 13:00:00', 1),\n(960, 8, 7, '2012-08-08 16:00:00', 1),\n(961, 8, 1, '2012-08-08 16:30:00', 1),\n(962, 8, 3, '2012-08-08 17:00:00', 1),\n(963, 8, 2, '2012-08-08 17:30:00', 1),\n(964, 8, 1, '2012-08-08 18:30:00', 1),\n(965, 0, 6, '2012-08-09 09:30:00', 3),\n(966, 0, 7, '2012-08-09 16:00:00', 3),\n(967, 0, 10, '2012-08-09 17:30:00', 3),\n(968, 1, 10, '2012-08-09 08:00:00', 3),\n(969, 1, 0, '2012-08-09 10:00:00', 3),\n(970, 1, 8, '2012-08-09 14:00:00', 3),\n(971, 1, 0, '2012-08-09 17:00:00', 3),\n(972, 2, 2, '2012-08-09 09:00:00', 3),\n(973, 2, 1, '2012-08-09 11:00:00', 3),\n(974, 2, 9, '2012-08-09 13:00:00', 3),\n(975, 2, 1, '2012-08-09 14:30:00', 3),\n(976, 2, 1, '2012-08-09 16:30:00', 3),\n(977, 3, 10, '2012-08-09 10:00:00', 2),\n(978, 3, 7, '2012-08-09 13:30:00', 2),\n(979, 3, 6, '2012-08-09 14:30:00', 2),\n(980, 3, 2, '2012-08-09 18:00:00', 2),\n(981, 4, 0, '2012-08-09 09:00:00', 4),\n(982, 4, 0, '2012-08-09 12:00:00', 4),\n(983, 4, 10, '2012-08-09 16:30:00', 2),\n(984, 4, 9, '2012-08-09 17:30:00', 2),\n(985, 4, 8, '2012-08-09 18:30:00', 2),\n(986, 4, 10, '2012-08-09 19:30:00', 2),\n(987, 6, 6, '2012-08-09 11:30:00', 2),\n(988, 6, 6, '2012-08-09 18:30:00', 2),\n(989, 7, 8, '2012-08-09 08:00:00', 2),\n(990, 7, 8, '2012-08-09 10:30:00', 2),\n(991, 7, 0, '2012-08-09 12:30:00', 2),\n(992, 7, 6, '2012-08-09 16:00:00', 2),\n(993, 7, 2, '2012-08-09 17:00:00', 2),\n(994, 7, 4, '2012-08-09 18:30:00', 2),\n(995, 8, 4, '2012-08-09 10:00:00', 1),\n(996, 8, 2, '2012-08-09 11:30:00', 1),\n(997, 8, 6, '2012-08-09 13:00:00', 1),\n(998, 8, 3, '2012-08-09 15:00:00', 1),\n(999, 8, 5, '2012-08-09 15:30:00', 1),\n(1000, 8, 3, '2012-08-09 17:30:00', 1),\n(1001, 0, 3, '2012-08-10 08:00:00', 3),\n(1002, 0, 2, '2012-08-10 09:30:00', 3),\n(1003, 0, 5, '2012-08-10 11:30:00', 3),\n(1004, 0, 2, '2012-08-10 13:00:00', 3),\n(1005, 0, 8, '2012-08-10 16:30:00', 3),\n(1006, 1, 10, '2012-08-10 08:30:00', 6),\n(1007, 1, 8, '2012-08-10 12:00:00', 3),\n(1008, 1, 9, '2012-08-10 14:00:00', 3),\n(1009, 1, 0, '2012-08-10 16:00:00', 3),\n(1010, 1, 10, '2012-08-10 18:30:00', 3),\n(1011, 2, 1, '2012-08-10 08:00:00', 3),\n(1012, 2, 8, '2012-08-10 09:30:00', 3),\n(1013, 2, 7, '2012-08-10 17:30:00', 3),\n(1014, 2, 0, '2012-08-10 19:00:00', 3),\n(1015, 3, 2, '2012-08-10 08:00:00', 2),\n(1016, 3, 7, '2012-08-10 10:30:00', 2),\n(1017, 3, 10, '2012-08-10 11:30:00', 2),\n(1018, 3, 4, '2012-08-10 14:30:00', 2),\n(1019, 3, 3, '2012-08-10 18:00:00', 4),\n(1020, 4, 6, '2012-08-10 08:30:00', 2),\n(1021, 4, 5, '2012-08-10 10:00:00', 2),\n(1022, 4, 6, '2012-08-10 12:00:00', 2),\n(1023, 4, 0, '2012-08-10 13:00:00', 2),\n(1024, 4, 8, '2012-08-10 14:00:00', 2),\n(1025, 4, 1, '2012-08-10 15:30:00', 2),\n(1026, 4, 3, '2012-08-10 16:30:00', 2),\n(1027, 4, 9, '2012-08-10 19:00:00', 2),\n(1028, 5, 0, '2012-08-10 13:30:00', 2),\n(1029, 6, 0, '2012-08-10 09:00:00', 2),\n(1030, 6, 0, '2012-08-10 11:00:00', 2),\n(1031, 6, 0, '2012-08-10 12:30:00', 2),\n(1032, 6, 0, '2012-08-10 15:00:00', 2),\n(1033, 6, 10, '2012-08-10 16:30:00', 2),\n(1034, 6, 0, '2012-08-10 18:00:00', 2),\n(1035, 7, 4, '2012-08-10 09:30:00', 2),\n(1036, 7, 4, '2012-08-10 11:00:00', 2),\n(1037, 7, 9, '2012-08-10 13:00:00', 2),\n(1038, 7, 6, '2012-08-10 15:00:00', 2),\n(1039, 7, 5, '2012-08-10 16:30:00', 4),\n(1040, 7, 6, '2012-08-10 18:30:00', 2),\n(1041, 7, 7, '2012-08-10 19:30:00', 2),\n(1042, 8, 8, '2012-08-10 09:00:00', 1),\n(1043, 8, 7, '2012-08-10 10:00:00', 1),\n(1044, 8, 3, '2012-08-10 11:30:00', 1),\n(1045, 8, 3, '2012-08-10 12:30:00', 1),\n(1046, 8, 7, '2012-08-10 14:00:00', 1),\n(1047, 8, 2, '2012-08-10 14:30:00', 1),\n(1048, 8, 2, '2012-08-10 15:30:00', 2),\n(1049, 8, 7, '2012-08-10 17:00:00', 1),\n(1050, 8, 4, '2012-08-10 17:30:00', 1),\n(1051, 8, 3, '2012-08-10 20:00:00', 1),\n(1052, 0, 0, '2012-08-11 08:00:00', 3),\n(1053, 0, 5, '2012-08-11 10:00:00', 3),\n(1054, 0, 0, '2012-08-11 12:00:00', 3),\n(1055, 0, 4, '2012-08-11 13:30:00', 3),\n(1056, 0, 0, '2012-08-11 15:00:00', 3),\n(1057, 0, 12, '2012-08-11 16:30:00', 3),\n(1058, 0, 4, '2012-08-11 18:30:00', 3),\n(1059, 1, 11, '2012-08-11 08:00:00', 3),\n(1060, 1, 0, '2012-08-11 10:00:00', 3),\n(1061, 1, 0, '2012-08-11 12:30:00', 3),\n(1062, 1, 0, '2012-08-11 14:30:00', 3),\n(1063, 1, 8, '2012-08-11 16:00:00', 3),\n(1064, 1, 0, '2012-08-11 17:30:00', 3),\n(1065, 2, 1, '2012-08-11 09:00:00', 3),\n(1066, 2, 7, '2012-08-11 11:00:00', 3),\n(1067, 2, 1, '2012-08-11 18:00:00', 3),\n(1068, 3, 11, '2012-08-11 12:00:00', 2),\n(1069, 3, 6, '2012-08-11 14:00:00', 2),\n(1070, 3, 7, '2012-08-11 17:30:00', 2),\n(1071, 3, 13, '2012-08-11 19:00:00', 2),\n(1072, 4, 0, '2012-08-11 10:00:00', 2),\n(1073, 4, 14, '2012-08-11 11:00:00', 2),\n(1074, 4, 0, '2012-08-11 12:30:00', 2),\n(1075, 4, 8, '2012-08-11 14:00:00', 2),\n(1076, 4, 6, '2012-08-11 16:30:00', 2),\n(1077, 4, 8, '2012-08-11 18:00:00', 2),\n(1078, 4, 9, '2012-08-11 19:00:00', 2),\n(1079, 5, 12, '2012-08-11 19:30:00', 2),\n(1080, 6, 13, '2012-08-11 08:00:00', 2),\n(1081, 6, 0, '2012-08-11 09:00:00', 2),\n(1082, 6, 0, '2012-08-11 14:00:00', 2),\n(1083, 6, 6, '2012-08-11 15:00:00', 2),\n(1084, 6, 6, '2012-08-11 17:30:00', 4),\n(1085, 7, 2, '2012-08-11 08:30:00', 2),\n(1086, 7, 8, '2012-08-11 11:30:00', 2),\n(1087, 7, 4, '2012-08-11 15:00:00', 2),\n(1088, 7, 2, '2012-08-11 16:00:00', 2),\n(1089, 7, 8, '2012-08-11 19:00:00', 2),\n(1090, 8, 3, '2012-08-11 08:00:00', 2),\n(1091, 8, 1, '2012-08-11 11:30:00', 1),\n(1092, 8, 3, '2012-08-11 12:00:00', 1),\n(1093, 8, 3, '2012-08-11 13:30:00', 3),\n(1094, 8, 3, '2012-08-11 16:00:00', 1),\n(1095, 8, 2, '2012-08-11 17:00:00', 1),\n(1096, 8, 3, '2012-08-11 17:30:00', 1),\n(1097, 8, 2, '2012-08-11 18:00:00', 1),\n(1098, 8, 14, '2012-08-11 19:00:00', 1),\n(1099, 0, 0, '2012-08-12 08:00:00', 3),\n(1100, 0, 7, '2012-08-12 10:30:00', 3),\n(1101, 0, 14, '2012-08-12 13:00:00', 3),\n(1102, 0, 0, '2012-08-12 14:30:00', 3),\n(1103, 0, 6, '2012-08-12 16:00:00', 3),\n(1104, 0, 0, '2012-08-12 17:30:00', 6),\n(1105, 1, 0, '2012-08-12 10:30:00', 3),\n(1106, 1, 9, '2012-08-12 13:30:00', 3),\n(1107, 1, 8, '2012-08-12 19:00:00', 3),\n(1108, 2, 1, '2012-08-12 11:30:00', 3),\n(1109, 2, 2, '2012-08-12 13:00:00', 3),\n(1110, 2, 0, '2012-08-12 14:30:00', 3),\n(1111, 3, 6, '2012-08-12 08:00:00', 2),\n(1112, 3, 10, '2012-08-12 09:30:00', 4),\n(1113, 3, 11, '2012-08-12 14:30:00', 2),\n(1114, 3, 3, '2012-08-12 17:00:00', 2),\n(1115, 3, 5, '2012-08-12 19:00:00', 2),\n(1116, 4, 0, '2012-08-12 09:30:00', 2),\n(1117, 4, 0, '2012-08-12 12:00:00', 2),\n(1118, 4, 6, '2012-08-12 13:00:00', 2),\n(1119, 4, 7, '2012-08-12 16:30:00', 2),\n(1120, 4, 6, '2012-08-12 18:00:00', 2),\n(1121, 5, 0, '2012-08-12 09:30:00', 2),\n(1122, 5, 0, '2012-08-12 12:30:00', 2),\n(1123, 6, 0, '2012-08-12 09:00:00', 4),\n(1124, 6, 13, '2012-08-12 13:00:00', 2),\n(1125, 6, 0, '2012-08-12 14:30:00', 2),\n(1126, 6, 10, '2012-08-12 17:00:00', 2),\n(1127, 7, 8, '2012-08-12 11:00:00', 2),\n(1128, 7, 2, '2012-08-12 12:00:00', 2),\n(1129, 7, 13, '2012-08-12 14:00:00', 2),\n(1130, 7, 5, '2012-08-12 15:00:00', 2),\n(1131, 8, 9, '2012-08-12 08:00:00', 1),\n(1132, 8, 11, '2012-08-12 10:30:00', 1),\n(1133, 8, 3, '2012-08-12 12:00:00', 1),\n(1134, 8, 8, '2012-08-12 12:30:00', 1),\n(1135, 8, 0, '2012-08-12 13:30:00', 1),\n(1136, 8, 0, '2012-08-12 14:30:00', 1),\n(1137, 8, 3, '2012-08-12 19:00:00', 1),\n(1138, 0, 4, '2012-08-13 08:30:00', 3),\n(1139, 0, 0, '2012-08-13 11:00:00', 3),\n(1140, 0, 6, '2012-08-13 15:30:00', 3),\n(1141, 0, 0, '2012-08-13 18:00:00', 3),\n(1142, 1, 12, '2012-08-13 08:30:00', 3),\n(1143, 1, 6, '2012-08-13 11:00:00', 3),\n(1144, 1, 10, '2012-08-13 12:30:00', 6),\n(1145, 1, 11, '2012-08-13 15:30:00', 3),\n(1146, 1, 0, '2012-08-13 17:00:00', 3),\n(1147, 1, 11, '2012-08-13 19:00:00', 3),\n(1148, 2, 1, '2012-08-13 08:00:00', 3),\n(1149, 2, 1, '2012-08-13 11:00:00', 3),\n(1150, 2, 11, '2012-08-13 13:00:00', 3),\n(1151, 2, 2, '2012-08-13 14:30:00', 3),\n(1152, 2, 1, '2012-08-13 17:00:00', 3),\n(1153, 2, 5, '2012-08-13 19:00:00', 3),\n(1154, 3, 3, '2012-08-13 08:00:00', 2),\n(1155, 3, 10, '2012-08-13 11:00:00', 2),\n(1156, 3, 9, '2012-08-13 12:00:00', 2),\n(1157, 3, 3, '2012-08-13 13:00:00', 2),\n(1158, 3, 10, '2012-08-13 15:30:00', 2),\n(1159, 3, 6, '2012-08-13 17:30:00', 2),\n(1160, 4, 7, '2012-08-13 08:00:00', 2),\n(1161, 4, 10, '2012-08-13 09:00:00', 2),\n(1162, 4, 0, '2012-08-13 10:30:00', 4),\n(1163, 4, 0, '2012-08-13 14:00:00', 2),\n(1164, 4, 8, '2012-08-13 15:00:00', 2),\n(1165, 4, 3, '2012-08-13 16:00:00', 2),\n(1166, 4, 10, '2012-08-13 19:00:00', 2),\n(1167, 6, 0, '2012-08-13 08:00:00', 2),\n(1168, 6, 5, '2012-08-13 12:00:00', 2),\n(1169, 6, 6, '2012-08-13 13:00:00', 2),\n(1170, 6, 0, '2012-08-13 17:30:00', 6),\n(1171, 7, 6, '2012-08-13 08:00:00', 2),\n(1172, 7, 14, '2012-08-13 09:00:00', 2),\n(1173, 7, 7, '2012-08-13 10:00:00', 2),\n(1174, 7, 13, '2012-08-13 13:00:00', 2),\n(1175, 7, 8, '2012-08-13 14:00:00', 2),\n(1176, 7, 9, '2012-08-13 17:00:00', 2),\n(1177, 7, 11, '2012-08-13 18:00:00', 2),\n(1178, 7, 4, '2012-08-13 19:00:00', 2),\n(1179, 8, 2, '2012-08-13 08:30:00', 1),\n(1180, 8, 1, '2012-08-13 10:00:00', 1),\n(1181, 8, 3, '2012-08-13 11:00:00', 1),\n(1182, 8, 4, '2012-08-13 12:30:00', 1),\n(1183, 8, 7, '2012-08-13 14:00:00', 1),\n(1184, 8, 4, '2012-08-13 15:00:00', 1),\n(1185, 8, 1, '2012-08-13 16:30:00', 1),\n(1186, 8, 6, '2012-08-13 17:00:00', 1),\n(1187, 8, 3, '2012-08-13 18:30:00', 2),\n(1188, 8, 3, '2012-08-13 20:00:00', 1),\n(1189, 0, 7, '2012-08-14 09:00:00', 3),\n(1190, 0, 14, '2012-08-14 10:30:00', 3),\n(1191, 0, 11, '2012-08-14 13:00:00', 3),\n(1192, 0, 0, '2012-08-14 15:00:00', 6),\n(1193, 0, 10, '2012-08-14 18:30:00', 3),\n(1194, 1, 11, '2012-08-14 10:00:00', 3),\n(1195, 1, 8, '2012-08-14 11:30:00', 3),\n(1196, 1, 0, '2012-08-14 16:30:00', 3),\n(1197, 1, 0, '2012-08-14 18:30:00', 3),\n(1198, 2, 13, '2012-08-14 08:00:00', 3),\n(1199, 2, 1, '2012-08-14 10:30:00', 3),\n(1200, 2, 1, '2012-08-14 13:00:00', 3),\n(1201, 2, 10, '2012-08-14 15:30:00', 3),\n(1202, 2, 0, '2012-08-14 17:00:00', 3),\n(1203, 2, 1, '2012-08-14 19:00:00', 3),\n(1204, 3, 10, '2012-08-14 10:00:00', 2),\n(1205, 3, 10, '2012-08-14 13:00:00', 2),\n(1206, 3, 3, '2012-08-14 18:30:00', 4),\n(1207, 4, 11, '2012-08-14 08:30:00', 2),\n(1208, 4, 0, '2012-08-14 11:00:00', 2),\n(1209, 4, 6, '2012-08-14 12:30:00', 2),\n(1210, 4, 0, '2012-08-14 14:30:00', 2),\n(1211, 4, 14, '2012-08-14 16:30:00', 2),\n(1212, 4, 0, '2012-08-14 18:00:00', 2),\n(1213, 4, 6, '2012-08-14 19:30:00', 2),\n(1214, 5, 0, '2012-08-14 12:00:00', 2),\n(1215, 5, 0, '2012-08-14 13:30:00', 2),\n(1216, 6, 12, '2012-08-14 09:00:00', 2),\n(1217, 6, 0, '2012-08-14 12:30:00', 4),\n(1218, 6, 0, '2012-08-14 16:00:00', 4),\n(1219, 7, 8, '2012-08-14 09:30:00', 4),\n(1220, 7, 2, '2012-08-14 11:30:00', 2),\n(1221, 8, 0, '2012-08-14 08:00:00', 1),\n(1222, 8, 2, '2012-08-14 08:30:00', 1),\n(1223, 8, 11, '2012-08-14 09:30:00', 1),\n(1224, 8, 3, '2012-08-14 11:00:00', 1),\n(1225, 8, 12, '2012-08-14 12:30:00', 1),\n(1226, 8, 3, '2012-08-14 13:30:00', 1),\n(1227, 8, 3, '2012-08-14 16:30:00', 2),\n(1228, 8, 9, '2012-08-14 18:30:00', 1),\n(1229, 8, 6, '2012-08-14 19:00:00', 1),\n(1230, 8, 8, '2012-08-14 19:30:00', 1),\n(1231, 8, 0, '2012-08-14 20:00:00', 1),\n(1232, 0, 0, '2012-08-15 08:00:00', 3),\n(1233, 0, 6, '2012-08-15 11:30:00', 3),\n(1234, 0, 5, '2012-08-15 13:00:00', 3),\n(1235, 0, 14, '2012-08-15 15:00:00', 3),\n(1236, 0, 0, '2012-08-15 16:30:00', 3),\n(1237, 0, 7, '2012-08-15 18:00:00', 3),\n(1238, 1, 0, '2012-08-15 08:00:00', 3),\n(1239, 1, 8, '2012-08-15 09:30:00', 3),\n(1240, 1, 12, '2012-08-15 11:30:00', 3),\n(1241, 1, 11, '2012-08-15 14:30:00', 3),\n(1242, 1, 12, '2012-08-15 16:30:00', 3),\n(1243, 1, 8, '2012-08-15 18:30:00', 3),\n(1244, 2, 1, '2012-08-15 08:00:00', 3),\n(1245, 2, 0, '2012-08-15 10:00:00', 3),\n(1246, 2, 10, '2012-08-15 12:00:00', 3),\n(1247, 2, 13, '2012-08-15 13:30:00', 3),\n(1248, 2, 1, '2012-08-15 15:30:00', 3),\n(1249, 2, 9, '2012-08-15 18:00:00', 3),\n(1250, 3, 3, '2012-08-15 11:00:00', 2),\n(1251, 3, 1, '2012-08-15 13:00:00', 2),\n(1252, 3, 3, '2012-08-15 14:00:00', 2),\n(1253, 3, 11, '2012-08-15 16:30:00', 2),\n(1254, 3, 10, '2012-08-15 18:00:00', 2),\n(1255, 3, 3, '2012-08-15 19:30:00', 2),\n(1256, 4, 0, '2012-08-15 08:30:00', 4),\n(1257, 4, 6, '2012-08-15 10:30:00', 2),\n(1258, 4, 0, '2012-08-15 13:00:00', 2),\n(1259, 4, 0, '2012-08-15 15:00:00', 2),\n(1260, 4, 9, '2012-08-15 16:30:00', 2),\n(1261, 4, 11, '2012-08-15 18:00:00', 2),\n(1262, 5, 0, '2012-08-15 12:00:00', 2),\n(1263, 5, 0, '2012-08-15 16:00:00', 2),\n(1264, 5, 11, '2012-08-15 19:00:00', 2),\n(1265, 6, 6, '2012-08-15 08:00:00', 2),\n(1266, 6, 0, '2012-08-15 10:00:00', 2),\n(1267, 6, 13, '2012-08-15 11:30:00', 2),\n(1268, 6, 11, '2012-08-15 12:30:00', 2),\n(1269, 6, 10, '2012-08-15 13:30:00', 2),\n(1270, 6, 8, '2012-08-15 15:30:00', 2),\n(1271, 6, 13, '2012-08-15 17:00:00', 2),\n(1272, 6, 12, '2012-08-15 18:00:00', 2),\n(1273, 7, 6, '2012-08-15 15:00:00', 2),\n(1274, 7, 8, '2012-08-15 17:30:00', 2),\n(1275, 8, 6, '2012-08-15 09:00:00', 1),\n(1276, 8, 3, '2012-08-15 10:30:00', 1),\n(1277, 8, 2, '2012-08-15 11:30:00', 1),\n(1278, 8, 3, '2012-08-15 13:00:00', 1),\n(1279, 8, 2, '2012-08-15 14:00:00', 2),\n(1280, 8, 3, '2012-08-15 15:30:00', 1),\n(1281, 8, 0, '2012-08-15 16:00:00', 1),\n(1282, 8, 8, '2012-08-15 17:00:00', 1),\n(1283, 8, 3, '2012-08-15 17:30:00', 1),\n(1284, 8, 14, '2012-08-15 19:00:00', 1),\n(1285, 8, 1, '2012-08-15 19:30:00', 1),\n(1286, 8, 6, '2012-08-15 20:00:00', 1),\n(1287, 0, 4, '2012-08-16 08:30:00', 3),\n(1288, 0, 0, '2012-08-16 11:00:00', 3),\n(1289, 0, 5, '2012-08-16 12:30:00', 3),\n(1290, 0, 14, '2012-08-16 14:00:00', 3),\n(1291, 0, 0, '2012-08-16 15:30:00', 3),\n(1292, 0, 0, '2012-08-16 17:30:00', 3),\n(1293, 1, 12, '2012-08-16 08:00:00', 3),\n(1294, 1, 0, '2012-08-16 13:00:00', 3),\n(1295, 1, 11, '2012-08-16 14:30:00', 3),\n(1296, 1, 8, '2012-08-16 16:30:00', 3),\n(1297, 1, 12, '2012-08-16 18:00:00', 3),\n(1298, 2, 5, '2012-08-16 08:30:00', 3),\n(1299, 2, 14, '2012-08-16 10:00:00', 3),\n(1300, 2, 1, '2012-08-16 13:00:00', 3),\n(1301, 2, 2, '2012-08-16 15:30:00', 3),\n(1302, 2, 9, '2012-08-16 17:00:00', 3),\n(1303, 2, 15, '2012-08-16 18:30:00', 3),\n(1304, 3, 6, '2012-08-16 11:00:00', 2),\n(1305, 3, 10, '2012-08-16 16:30:00', 2),\n(1306, 3, 3, '2012-08-16 17:30:00', 2),\n(1307, 4, 1, '2012-08-16 08:30:00', 2),\n(1308, 4, 0, '2012-08-16 11:00:00', 2),\n(1309, 4, 1, '2012-08-16 12:00:00', 2),\n(1310, 4, 9, '2012-08-16 13:00:00', 2),\n(1311, 4, 8, '2012-08-16 14:00:00', 2),\n(1312, 4, 14, '2012-08-16 15:30:00', 2),\n(1313, 4, 13, '2012-08-16 18:30:00', 2),\n(1314, 4, 8, '2012-08-16 19:30:00', 2),\n(1315, 5, 0, '2012-08-16 11:00:00', 2),\n(1316, 6, 0, '2012-08-16 08:30:00', 2),\n(1317, 6, 0, '2012-08-16 11:30:00', 6),\n(1318, 6, 12, '2012-08-16 15:30:00', 2),\n(1319, 6, 5, '2012-08-16 17:30:00', 2),\n(1320, 6, 0, '2012-08-16 18:30:00', 2),\n(1321, 7, 7, '2012-08-16 10:30:00', 2),\n(1322, 7, 7, '2012-08-16 13:00:00', 2),\n(1323, 7, 13, '2012-08-16 14:30:00', 4),\n(1324, 7, 4, '2012-08-16 16:30:00', 2),\n(1325, 7, 10, '2012-08-16 18:00:00', 2),\n(1326, 8, 7, '2012-08-16 08:30:00', 1),\n(1327, 8, 3, '2012-08-16 09:00:00', 2),\n(1328, 8, 3, '2012-08-16 10:30:00', 1),\n(1329, 8, 3, '2012-08-16 12:00:00', 1),\n(1330, 8, 12, '2012-08-16 13:30:00', 1),\n(1331, 8, 3, '2012-08-16 14:00:00', 1),\n(1332, 8, 15, '2012-08-16 14:30:00', 1),\n(1333, 8, 3, '2012-08-16 15:00:00', 2),\n(1334, 8, 4, '2012-08-16 16:00:00', 1),\n(1335, 8, 3, '2012-08-16 19:00:00', 1),\n(1336, 8, 12, '2012-08-16 19:30:00', 1),\n(1337, 0, 0, '2012-08-17 08:30:00', 3),\n(1338, 0, 14, '2012-08-17 12:30:00', 3),\n(1339, 0, 6, '2012-08-17 14:00:00', 3),\n(1340, 0, 10, '2012-08-17 16:00:00', 3),\n(1341, 0, 14, '2012-08-17 17:30:00', 3),\n(1342, 1, 10, '2012-08-17 08:30:00', 3),\n(1343, 1, 0, '2012-08-17 11:00:00', 6),\n(1344, 1, 11, '2012-08-17 15:00:00', 3),\n(1345, 1, 0, '2012-08-17 17:00:00', 3),\n(1346, 1, 16, '2012-08-17 19:00:00', 3),\n(1347, 2, 1, '2012-08-17 09:00:00', 3),\n(1348, 2, 1, '2012-08-17 12:00:00', 3),\n(1349, 2, 11, '2012-08-17 13:30:00', 3),\n(1350, 2, 2, '2012-08-17 16:30:00', 3),\n(1351, 2, 1, '2012-08-17 18:30:00', 3),\n(1352, 3, 10, '2012-08-17 10:00:00', 2),\n(1353, 3, 15, '2012-08-17 11:00:00', 2),\n(1354, 3, 13, '2012-08-17 14:00:00', 2),\n(1355, 3, 15, '2012-08-17 17:30:00', 2),\n(1356, 4, 9, '2012-08-17 08:00:00', 2),\n(1357, 4, 6, '2012-08-17 09:30:00', 2),\n(1358, 4, 3, '2012-08-17 12:00:00', 2),\n(1359, 4, 3, '2012-08-17 13:30:00', 2),\n(1360, 4, 0, '2012-08-17 14:30:00', 2),\n(1361, 4, 16, '2012-08-17 15:30:00', 2),\n(1362, 4, 0, '2012-08-17 16:30:00', 4),\n(1363, 4, 9, '2012-08-17 19:00:00', 2),\n(1364, 5, 4, '2012-08-17 13:00:00', 2),\n(1365, 5, 0, '2012-08-17 15:30:00', 2),\n(1366, 6, 0, '2012-08-17 08:00:00', 2),\n(1367, 6, 12, '2012-08-17 09:30:00', 2),\n(1368, 6, 0, '2012-08-17 11:00:00', 2),\n(1369, 6, 6, '2012-08-17 12:00:00', 2),\n(1370, 6, 0, '2012-08-17 15:00:00', 2),\n(1371, 6, 0, '2012-08-17 17:30:00', 2),\n(1372, 6, 12, '2012-08-17 18:30:00', 2),\n(1373, 6, 6, '2012-08-17 19:30:00', 2),\n(1374, 7, 8, '2012-08-17 08:30:00', 2),\n(1375, 7, 13, '2012-08-17 12:30:00', 2),\n(1376, 7, 15, '2012-08-17 14:30:00', 2),\n(1377, 7, 7, '2012-08-17 16:30:00', 2),\n(1378, 8, 16, '2012-08-17 08:30:00', 1),\n(1379, 8, 16, '2012-08-17 10:00:00', 1),\n(1380, 8, 14, '2012-08-17 11:00:00', 1),\n(1381, 8, 0, '2012-08-17 12:00:00', 1),\n(1382, 8, 2, '2012-08-17 14:00:00', 1),\n(1383, 8, 3, '2012-08-17 14:30:00', 1),\n(1384, 8, 3, '2012-08-17 15:30:00', 1),\n(1385, 8, 8, '2012-08-17 16:00:00', 1),\n(1386, 8, 16, '2012-08-17 16:30:00', 1),\n(1387, 8, 4, '2012-08-17 18:00:00', 1),\n(1388, 8, 6, '2012-08-17 18:30:00', 1),\n(1389, 8, 12, '2012-08-17 19:30:00', 1),\n(1390, 0, 5, '2012-08-18 08:00:00', 3),\n(1391, 0, 0, '2012-08-18 11:00:00', 3),\n(1392, 0, 5, '2012-08-18 12:30:00', 3),\n(1393, 0, 0, '2012-08-18 14:00:00', 3),\n(1394, 1, 8, '2012-08-18 09:30:00', 3),\n(1395, 1, 15, '2012-08-18 12:30:00', 3),\n(1396, 1, 0, '2012-08-18 14:30:00', 3),\n(1397, 1, 7, '2012-08-18 17:00:00', 3),\n(1398, 1, 12, '2012-08-18 18:30:00', 3),\n(1399, 2, 1, '2012-08-18 08:30:00', 3),\n(1400, 2, 1, '2012-08-18 11:30:00', 3),\n(1401, 2, 2, '2012-08-18 16:00:00', 3),\n(1402, 2, 14, '2012-08-18 18:00:00', 3),\n(1403, 3, 15, '2012-08-18 08:00:00', 2),\n(1404, 3, 15, '2012-08-18 11:00:00', 2),\n(1405, 3, 12, '2012-08-18 13:30:00', 2),\n(1406, 3, 1, '2012-08-18 19:30:00', 2),\n(1407, 4, 16, '2012-08-18 08:00:00', 2),\n(1408, 4, 3, '2012-08-18 09:00:00', 2),\n(1409, 4, 4, '2012-08-18 10:30:00', 2),\n(1410, 4, 3, '2012-08-18 11:30:00', 2),\n(1411, 4, 11, '2012-08-18 12:30:00', 2),\n(1412, 4, 0, '2012-08-18 13:30:00', 2),\n(1413, 4, 0, '2012-08-18 15:00:00', 4),\n(1414, 4, 5, '2012-08-18 17:30:00', 2),\n(1415, 4, 0, '2012-08-18 18:30:00', 4),\n(1416, 5, 0, '2012-08-18 11:00:00', 4),\n(1417, 6, 12, '2012-08-18 09:00:00', 2),\n(1418, 6, 0, '2012-08-18 11:00:00', 2),\n(1419, 6, 4, '2012-08-18 12:00:00', 2),\n(1420, 6, 0, '2012-08-18 13:00:00', 2),\n(1421, 6, 14, '2012-08-18 14:30:00', 2),\n(1422, 6, 0, '2012-08-18 16:30:00', 4),\n(1423, 6, 8, '2012-08-18 19:30:00', 2),\n(1424, 7, 8, '2012-08-18 12:00:00', 2),\n(1425, 7, 8, '2012-08-18 13:30:00', 2),\n(1426, 7, 15, '2012-08-18 15:00:00', 2),\n(1427, 7, 15, '2012-08-18 16:30:00', 2),\n(1428, 7, 1, '2012-08-18 18:30:00', 2),\n(1429, 8, 3, '2012-08-18 08:00:00', 1),\n(1430, 8, 6, '2012-08-18 08:30:00', 1),\n(1431, 8, 16, '2012-08-18 09:30:00', 1),\n(1432, 8, 16, '2012-08-18 11:30:00', 2),\n(1433, 8, 2, '2012-08-18 12:30:00', 1),\n(1434, 8, 16, '2012-08-18 13:00:00', 1),\n(1435, 8, 11, '2012-08-18 13:30:00', 1),\n(1436, 8, 16, '2012-08-18 14:00:00', 2),\n(1437, 8, 0, '2012-08-18 16:00:00', 1),\n(1438, 8, 3, '2012-08-18 16:30:00', 1),\n(1439, 0, 12, '2012-08-19 08:00:00', 3),\n(1440, 0, 16, '2012-08-19 10:30:00', 3),\n(1441, 0, 6, '2012-08-19 13:30:00', 3),\n(1442, 0, 6, '2012-08-19 17:30:00', 3),\n(1443, 1, 10, '2012-08-19 08:00:00', 3),\n(1444, 1, 7, '2012-08-19 11:00:00', 3),\n(1445, 1, 10, '2012-08-19 12:30:00', 3),\n(1446, 1, 0, '2012-08-19 15:30:00', 3),\n(1447, 2, 1, '2012-08-19 09:00:00', 3),\n(1448, 2, 5, '2012-08-19 12:30:00', 3),\n(1449, 2, 14, '2012-08-19 16:30:00', 3),\n(1450, 2, 2, '2012-08-19 18:00:00', 3),\n(1451, 3, 16, '2012-08-19 08:00:00', 2),\n(1452, 3, 10, '2012-08-19 09:30:00', 2),\n(1453, 3, 15, '2012-08-19 11:00:00', 4),\n(1454, 3, 14, '2012-08-19 15:00:00', 2),\n(1455, 3, 3, '2012-08-19 18:30:00', 2),\n(1456, 4, 0, '2012-08-19 09:30:00', 6),\n(1457, 4, 5, '2012-08-19 14:00:00', 2),\n(1458, 4, 1, '2012-08-19 15:30:00', 2),\n(1459, 4, 5, '2012-08-19 16:30:00', 2),\n(1460, 4, 16, '2012-08-19 17:30:00', 2),\n(1461, 4, 1, '2012-08-19 19:00:00', 2),\n(1462, 5, 0, '2012-08-19 17:30:00', 2),\n(1463, 5, 0, '2012-08-19 19:00:00', 2),\n(1464, 6, 0, '2012-08-19 09:00:00', 2),\n(1465, 6, 12, '2012-08-19 10:00:00', 2),\n(1466, 6, 0, '2012-08-19 12:00:00', 2),\n(1467, 6, 11, '2012-08-19 13:30:00', 2),\n(1468, 6, 16, '2012-08-19 14:30:00', 2),\n(1469, 6, 0, '2012-08-19 16:30:00', 2),\n(1470, 6, 0, '2012-08-19 18:30:00', 4),\n(1471, 7, 6, '2012-08-19 08:00:00', 2),\n(1472, 7, 5, '2012-08-19 11:00:00', 2),\n(1473, 7, 13, '2012-08-19 13:30:00', 2),\n(1474, 7, 15, '2012-08-19 17:00:00', 2),\n(1475, 7, 4, '2012-08-19 18:30:00', 2),\n(1476, 8, 0, '2012-08-19 10:00:00', 1),\n(1477, 8, 3, '2012-08-19 11:00:00', 1),\n(1478, 8, 4, '2012-08-19 12:30:00', 1),\n(1479, 8, 6, '2012-08-19 13:00:00', 1),\n(1480, 8, 1, '2012-08-19 15:00:00', 1),\n(1481, 8, 16, '2012-08-19 15:30:00', 1),\n(1482, 8, 2, '2012-08-19 17:00:00', 1),\n(1483, 8, 8, '2012-08-19 17:30:00', 1),\n(1484, 8, 12, '2012-08-19 19:00:00', 1),\n(1485, 0, 10, '2012-08-20 08:30:00', 3),\n(1486, 0, 10, '2012-08-20 10:30:00', 3),\n(1487, 0, 14, '2012-08-20 12:00:00', 3),\n(1488, 0, 4, '2012-08-20 14:30:00', 3),\n(1489, 0, 14, '2012-08-20 16:30:00', 3),\n(1490, 0, 16, '2012-08-20 19:00:00', 3),\n(1491, 1, 9, '2012-08-20 08:00:00', 3),\n(1492, 1, 16, '2012-08-20 09:30:00', 3),\n(1493, 1, 0, '2012-08-20 12:00:00', 3),\n(1494, 1, 10, '2012-08-20 13:30:00', 6),\n(1495, 1, 6, '2012-08-20 16:30:00', 3),\n(1496, 1, 8, '2012-08-20 18:30:00', 3),\n(1497, 2, 8, '2012-08-20 08:30:00', 3),\n(1498, 2, 5, '2012-08-20 10:00:00', 6),\n(1499, 2, 1, '2012-08-20 13:00:00', 3),\n(1500, 2, 1, '2012-08-20 15:00:00', 6),\n(1501, 2, 14, '2012-08-20 19:00:00', 3),\n(1502, 3, 1, '2012-08-20 08:00:00', 2),\n(1503, 3, 4, '2012-08-20 11:00:00', 2),\n(1504, 3, 8, '2012-08-20 12:30:00', 2),\n(1505, 3, 11, '2012-08-20 15:30:00', 2),\n(1506, 3, 3, '2012-08-20 17:30:00', 2),\n(1507, 4, 6, '2012-08-20 08:30:00', 2),\n(1508, 4, 3, '2012-08-20 09:30:00', 2),\n(1509, 4, 6, '2012-08-20 10:30:00', 2),\n(1510, 4, 13, '2012-08-20 11:30:00', 2),\n(1511, 4, 16, '2012-08-20 12:30:00', 2),\n(1512, 4, 5, '2012-08-20 13:30:00', 2),\n(1513, 4, 0, '2012-08-20 14:30:00', 2),\n(1514, 4, 16, '2012-08-20 16:00:00', 2),\n(1515, 4, 16, '2012-08-20 17:30:00', 2),\n(1516, 4, 0, '2012-08-20 18:30:00', 2),\n(1517, 6, 16, '2012-08-20 08:00:00', 2),\n(1518, 6, 13, '2012-08-20 09:00:00', 2),\n(1519, 6, 0, '2012-08-20 10:30:00', 2),\n(1520, 6, 6, '2012-08-20 11:30:00', 2),\n(1521, 6, 11, '2012-08-20 12:30:00', 2),\n(1522, 6, 0, '2012-08-20 14:30:00', 2),\n(1523, 6, 8, '2012-08-20 16:30:00', 2),\n(1524, 6, 6, '2012-08-20 19:00:00', 2),\n(1525, 7, 5, '2012-08-20 08:00:00', 2),\n(1526, 7, 1, '2012-08-20 11:30:00', 2),\n(1527, 7, 17, '2012-08-20 12:30:00', 2),\n(1528, 7, 6, '2012-08-20 14:30:00', 2),\n(1529, 7, 9, '2012-08-20 16:00:00', 2),\n(1530, 7, 17, '2012-08-20 17:30:00', 2),\n(1531, 8, 15, '2012-08-20 10:30:00', 1),\n(1532, 8, 3, '2012-08-20 11:30:00', 1),\n(1533, 8, 0, '2012-08-20 13:30:00', 1),\n(1534, 8, 2, '2012-08-20 14:00:00', 1),\n(1535, 8, 3, '2012-08-20 17:00:00', 1),\n(1536, 8, 2, '2012-08-20 18:00:00', 1),\n(1537, 0, 14, '2012-08-21 09:00:00', 6),\n(1538, 0, 0, '2012-08-21 13:00:00', 3),\n(1539, 0, 0, '2012-08-21 18:00:00', 3),\n(1540, 1, 11, '2012-08-21 09:30:00', 3),\n(1541, 1, 9, '2012-08-21 11:00:00', 3),\n(1542, 1, 10, '2012-08-21 12:30:00', 3),\n(1543, 1, 7, '2012-08-21 14:00:00', 3),\n(1544, 1, 10, '2012-08-21 16:30:00', 3),\n(1545, 2, 15, '2012-08-21 08:00:00', 3),\n(1546, 2, 1, '2012-08-21 09:30:00', 3),\n(1547, 2, 17, '2012-08-21 11:00:00', 3),\n(1548, 2, 2, '2012-08-21 12:30:00', 3),\n(1549, 2, 1, '2012-08-21 15:30:00', 3),\n(1550, 2, 15, '2012-08-21 17:00:00', 3),\n(1551, 3, 8, '2012-08-21 10:30:00', 2),\n(1552, 3, 16, '2012-08-21 12:00:00', 2),\n(1553, 3, 2, '2012-08-21 16:00:00', 2),\n(1554, 3, 1, '2012-08-21 18:30:00', 2),\n(1555, 4, 0, '2012-08-21 08:30:00', 2),\n(1556, 4, 7, '2012-08-21 10:00:00', 2),\n(1557, 4, 13, '2012-08-21 11:00:00', 2),\n(1558, 4, 14, '2012-08-21 12:00:00', 2),\n(1559, 4, 0, '2012-08-21 13:00:00', 2),\n(1560, 4, 16, '2012-08-21 14:30:00', 2),\n(1561, 4, 0, '2012-08-21 16:30:00', 2),\n(1562, 4, 0, '2012-08-21 18:00:00', 2),\n(1563, 5, 0, '2012-08-21 08:00:00', 2),\n(1564, 5, 0, '2012-08-21 18:30:00', 2),\n(1565, 6, 0, '2012-08-21 09:00:00', 2),\n(1566, 6, 0, '2012-08-21 10:30:00', 4),\n(1567, 6, 0, '2012-08-21 14:00:00', 2),\n(1568, 6, 0, '2012-08-21 15:30:00', 2),\n(1569, 6, 0, '2012-08-21 17:00:00', 2),\n(1570, 6, 0, '2012-08-21 19:00:00', 2),\n(1571, 7, 10, '2012-08-21 09:30:00', 2),\n(1572, 7, 13, '2012-08-21 13:00:00', 2),\n(1573, 7, 5, '2012-08-21 15:30:00', 2),\n(1574, 7, 5, '2012-08-21 17:30:00', 2),\n(1575, 8, 11, '2012-08-21 08:00:00', 1),\n(1576, 8, 6, '2012-08-21 09:00:00', 1),\n(1577, 8, 3, '2012-08-21 09:30:00', 1),\n(1578, 8, 16, '2012-08-21 10:00:00', 1),\n(1579, 8, 6, '2012-08-21 10:30:00', 1),\n(1580, 8, 3, '2012-08-21 11:00:00', 1),\n(1581, 8, 3, '2012-08-21 12:00:00', 1),\n(1582, 8, 3, '2012-08-21 13:00:00', 1),\n(1583, 8, 6, '2012-08-21 13:30:00', 1),\n(1584, 8, 16, '2012-08-21 16:00:00', 2),\n(1585, 8, 1, '2012-08-21 19:30:00', 1),\n(1586, 0, 11, '2012-08-22 08:00:00', 3),\n(1587, 0, 5, '2012-08-22 10:00:00', 3),\n(1588, 0, 0, '2012-08-22 11:30:00', 6),\n(1589, 0, 16, '2012-08-22 15:00:00', 6),\n(1590, 0, 11, '2012-08-22 18:00:00', 3),\n(1591, 1, 0, '2012-08-22 08:30:00', 3),\n(1592, 1, 0, '2012-08-22 10:30:00', 3),\n(1593, 1, 0, '2012-08-22 13:00:00', 3),\n(1594, 1, 7, '2012-08-22 15:00:00', 3),\n(1595, 1, 12, '2012-08-22 17:00:00', 3),\n(1596, 2, 10, '2012-08-22 09:00:00', 3),\n(1597, 2, 0, '2012-08-22 10:30:00', 3),\n(1598, 2, 1, '2012-08-22 12:30:00', 3),\n(1599, 2, 11, '2012-08-22 15:00:00', 3),\n(1600, 2, 0, '2012-08-22 16:30:00', 6),\n(1601, 3, 11, '2012-08-22 10:00:00', 2),\n(1602, 3, 3, '2012-08-22 11:30:00', 2),\n(1603, 3, 13, '2012-08-22 13:00:00', 2),\n(1604, 3, 1, '2012-08-22 14:30:00', 2),\n(1605, 3, 17, '2012-08-22 15:30:00', 2),\n(1606, 3, 15, '2012-08-22 16:30:00', 2),\n(1607, 3, 10, '2012-08-22 18:30:00', 2),\n(1608, 3, 15, '2012-08-22 19:30:00', 2),\n(1609, 4, 5, '2012-08-22 08:00:00', 2),\n(1610, 4, 9, '2012-08-22 09:00:00', 2),\n(1611, 4, 14, '2012-08-22 10:00:00', 2),\n(1612, 4, 0, '2012-08-22 11:00:00', 2),\n(1613, 4, 9, '2012-08-22 12:00:00', 2),\n(1614, 4, 0, '2012-08-22 14:00:00', 2),\n(1615, 4, 13, '2012-08-22 15:00:00', 2),\n(1616, 4, 3, '2012-08-22 16:00:00', 2),\n(1617, 4, 6, '2012-08-22 17:00:00', 2),\n(1618, 4, 0, '2012-08-22 18:00:00', 4),\n(1619, 5, 0, '2012-08-22 18:00:00', 2),\n(1620, 6, 8, '2012-08-22 08:30:00', 2),\n(1621, 6, 0, '2012-08-22 09:30:00', 2),\n(1622, 6, 12, '2012-08-22 11:00:00', 2),\n(1623, 6, 0, '2012-08-22 12:00:00', 4),\n(1624, 6, 6, '2012-08-22 14:00:00', 2),\n(1625, 6, 12, '2012-08-22 15:30:00', 2),\n(1626, 6, 0, '2012-08-22 18:00:00', 4),\n(1627, 7, 6, '2012-08-22 09:30:00', 2),\n(1628, 7, 4, '2012-08-22 11:30:00', 2),\n(1629, 7, 8, '2012-08-22 15:00:00', 2),\n(1630, 7, 1, '2012-08-22 16:00:00', 2),\n(1631, 7, 13, '2012-08-22 17:30:00', 2),\n(1632, 8, 8, '2012-08-22 08:00:00', 1),\n(1633, 8, 7, '2012-08-22 11:30:00', 1),\n(1634, 8, 8, '2012-08-22 12:00:00', 1),\n(1635, 8, 6, '2012-08-22 12:30:00', 1),\n(1636, 8, 3, '2012-08-22 15:00:00', 1),\n(1637, 8, 2, '2012-08-22 15:30:00', 1),\n(1638, 8, 15, '2012-08-22 16:00:00', 1),\n(1639, 8, 2, '2012-08-22 17:00:00', 1),\n(1640, 8, 3, '2012-08-22 19:00:00', 1),\n(1641, 8, 4, '2012-08-22 19:30:00', 1),\n(1642, 8, 9, '2012-08-22 20:00:00', 1),\n(1643, 0, 11, '2012-08-23 08:30:00', 3),\n(1644, 0, 14, '2012-08-23 11:30:00', 3),\n(1645, 0, 10, '2012-08-23 13:00:00', 3),\n(1646, 0, 5, '2012-08-23 15:30:00', 3),\n(1647, 0, 12, '2012-08-23 17:00:00', 3),\n(1648, 1, 12, '2012-08-23 09:00:00', 3),\n(1649, 1, 11, '2012-08-23 10:30:00', 3),\n(1650, 1, 0, '2012-08-23 13:00:00', 3),\n(1651, 1, 16, '2012-08-23 14:30:00', 3),\n(1652, 1, 10, '2012-08-23 16:00:00', 3),\n(1653, 1, 9, '2012-08-23 17:30:00', 3),\n(1654, 1, 15, '2012-08-23 19:00:00', 3),\n(1655, 2, 14, '2012-08-23 09:30:00', 3),\n(1656, 2, 1, '2012-08-23 11:00:00', 3),\n(1657, 2, 9, '2012-08-23 13:30:00', 3),\n(1658, 2, 8, '2012-08-23 15:30:00', 3),\n(1659, 3, 15, '2012-08-23 09:30:00', 2),\n(1660, 3, 3, '2012-08-23 10:30:00', 2),\n(1661, 3, 4, '2012-08-23 14:00:00', 2),\n(1662, 3, 1, '2012-08-23 15:00:00', 2),\n(1663, 3, 17, '2012-08-23 16:00:00', 2),\n(1664, 3, 3, '2012-08-23 17:00:00', 2),\n(1665, 3, 16, '2012-08-23 19:00:00', 2),\n(1666, 4, 0, '2012-08-23 08:30:00', 4),\n(1667, 4, 0, '2012-08-23 11:00:00', 2),\n(1668, 4, 11, '2012-08-23 12:00:00', 2),\n(1669, 4, 1, '2012-08-23 13:00:00', 2),\n(1670, 4, 5, '2012-08-23 14:30:00', 2),\n(1671, 4, 14, '2012-08-23 15:30:00', 2),\n(1672, 4, 2, '2012-08-23 16:30:00', 2),\n(1673, 4, 8, '2012-08-23 17:30:00', 2),\n(1674, 4, 3, '2012-08-23 18:30:00', 2),\n(1675, 5, 0, '2012-08-23 12:00:00', 2),\n(1676, 5, 0, '2012-08-23 16:30:00', 2),\n(1677, 6, 1, '2012-08-23 08:00:00', 2),\n(1678, 6, 0, '2012-08-23 09:00:00', 4),\n(1679, 6, 13, '2012-08-23 13:00:00', 2),\n(1680, 6, 12, '2012-08-23 14:00:00', 4),\n(1681, 6, 17, '2012-08-23 18:00:00', 2),\n(1682, 7, 4, '2012-08-23 11:00:00', 2),\n(1683, 7, 8, '2012-08-23 14:00:00', 2),\n(1684, 7, 13, '2012-08-23 16:00:00', 2),\n(1685, 7, 11, '2012-08-23 17:00:00', 2),\n(1686, 7, 10, '2012-08-23 18:30:00', 2),\n(1687, 7, 6, '2012-08-23 19:30:00', 2),\n(1688, 8, 17, '2012-08-23 09:00:00', 1),\n(1689, 8, 16, '2012-08-23 09:30:00', 1),\n(1690, 8, 6, '2012-08-23 10:00:00', 1),\n(1691, 8, 4, '2012-08-23 10:30:00', 1),\n(1692, 8, 3, '2012-08-23 13:00:00', 1),\n(1693, 8, 3, '2012-08-23 14:30:00', 2),\n(1694, 8, 9, '2012-08-23 15:30:00', 1),\n(1695, 8, 1, '2012-08-23 16:00:00', 1),\n(1696, 8, 16, '2012-08-23 17:30:00', 1),\n(1697, 8, 16, '2012-08-23 18:30:00', 1),\n(1698, 8, 17, '2012-08-23 19:00:00', 1),\n(1699, 8, 16, '2012-08-23 20:00:00', 1),\n(1700, 0, 14, '2012-08-24 09:00:00', 3),\n(1701, 0, 2, '2012-08-24 11:00:00', 3),\n(1702, 0, 0, '2012-08-24 12:30:00', 6),\n(1703, 0, 6, '2012-08-24 15:30:00', 3),\n(1704, 0, 16, '2012-08-24 17:00:00', 3),\n(1705, 0, 8, '2012-08-24 19:00:00', 3),\n(1706, 1, 12, '2012-08-24 08:00:00', 3),\n(1707, 1, 9, '2012-08-24 09:30:00', 3),\n(1708, 1, 0, '2012-08-24 11:30:00', 3),\n(1709, 1, 8, '2012-08-24 13:00:00', 3),\n(1710, 1, 10, '2012-08-24 15:30:00', 3),\n(1711, 1, 12, '2012-08-24 18:00:00', 3),\n(1712, 2, 13, '2012-08-24 08:00:00', 3),\n(1713, 2, 0, '2012-08-24 11:00:00', 3),\n(1714, 2, 15, '2012-08-24 13:00:00', 3),\n(1715, 2, 16, '2012-08-24 15:00:00', 3),\n(1716, 2, 12, '2012-08-24 16:30:00', 3),\n(1717, 3, 1, '2012-08-24 08:30:00', 2),\n(1718, 3, 3, '2012-08-24 11:00:00', 2),\n(1719, 3, 17, '2012-08-24 14:00:00', 2),\n(1720, 3, 8, '2012-08-24 16:30:00', 2),\n(1721, 3, 15, '2012-08-24 17:30:00', 2),\n(1722, 3, 10, '2012-08-24 18:30:00', 2),\n(1723, 4, 0, '2012-08-24 08:00:00', 2),\n(1724, 4, 3, '2012-08-24 10:00:00', 2),\n(1725, 4, 9, '2012-08-24 12:00:00', 2),\n(1726, 4, 14, '2012-08-24 13:00:00', 2),\n(1727, 4, 3, '2012-08-24 14:00:00', 2),\n(1728, 4, 0, '2012-08-24 17:00:00', 2),\n(1729, 4, 3, '2012-08-24 18:00:00', 2),\n(1730, 4, 0, '2012-08-24 19:00:00', 2),\n(1731, 5, 0, '2012-08-24 18:30:00', 2),\n(1732, 6, 6, '2012-08-24 09:30:00', 2),\n(1733, 6, 0, '2012-08-24 11:00:00', 2),\n(1734, 6, 14, '2012-08-24 12:00:00', 2),\n(1735, 6, 0, '2012-08-24 14:30:00', 2),\n(1736, 6, 11, '2012-08-24 17:00:00', 2),\n(1737, 6, 14, '2012-08-24 18:30:00', 2),\n(1738, 6, 0, '2012-08-24 19:30:00', 2),\n(1739, 7, 15, '2012-08-24 09:30:00', 2),\n(1740, 7, 17, '2012-08-24 13:00:00', 2),\n(1741, 7, 13, '2012-08-24 14:00:00', 2),\n(1742, 7, 4, '2012-08-24 17:00:00', 2),\n(1743, 7, 2, '2012-08-24 18:30:00', 2),\n(1744, 8, 3, '2012-08-24 08:30:00', 1),\n(1745, 8, 16, '2012-08-24 11:00:00', 1),\n(1746, 8, 16, '2012-08-24 13:30:00', 1),\n(1747, 8, 14, '2012-08-24 14:00:00', 1),\n(1748, 8, 14, '2012-08-24 17:30:00', 1),\n(1749, 0, 8, '2012-08-25 08:00:00', 3),\n(1750, 0, 7, '2012-08-25 11:00:00', 3),\n(1751, 0, 0, '2012-08-25 12:30:00', 3),\n(1752, 0, 5, '2012-08-25 14:00:00', 3),\n(1753, 0, 0, '2012-08-25 15:30:00', 3),\n(1754, 0, 17, '2012-08-25 17:00:00', 3),\n(1755, 1, 9, '2012-08-25 08:00:00', 3),\n(1756, 1, 11, '2012-08-25 11:30:00', 3),\n(1757, 1, 0, '2012-08-25 13:30:00', 9),\n(1758, 1, 15, '2012-08-25 18:30:00', 3),\n(1759, 2, 2, '2012-08-25 08:00:00', 3),\n(1760, 2, 1, '2012-08-25 09:30:00', 3),\n(1761, 2, 14, '2012-08-25 11:00:00', 3),\n(1762, 2, 1, '2012-08-25 12:30:00', 3),\n(1763, 2, 1, '2012-08-25 16:30:00', 3),\n(1764, 3, 16, '2012-08-25 08:00:00', 2),\n(1765, 3, 16, '2012-08-25 09:30:00', 2),\n(1766, 3, 0, '2012-08-25 12:00:00', 2),\n(1767, 3, 15, '2012-08-25 14:30:00', 2),\n(1768, 3, 11, '2012-08-25 18:30:00', 2),\n(1769, 3, 3, '2012-08-25 19:30:00', 2),\n(1770, 4, 14, '2012-08-25 08:00:00', 2),\n(1771, 4, 0, '2012-08-25 09:30:00', 2),\n(1772, 4, 6, '2012-08-25 10:30:00', 2),\n(1773, 4, 10, '2012-08-25 11:30:00', 2),\n(1774, 4, 3, '2012-08-25 12:30:00', 2),\n(1775, 4, 11, '2012-08-25 14:00:00', 2),\n(1776, 4, 13, '2012-08-25 15:30:00', 4),\n(1777, 4, 3, '2012-08-25 17:30:00', 2),\n(1778, 5, 11, '2012-08-25 08:00:00', 2),\n(1779, 5, 0, '2012-08-25 14:30:00', 2),\n(1780, 6, 0, '2012-08-25 08:30:00', 4),\n(1781, 6, 0, '2012-08-25 11:00:00', 2),\n(1782, 6, 12, '2012-08-25 14:00:00', 2),\n(1783, 6, 0, '2012-08-25 18:30:00', 2),\n(1784, 6, 6, '2012-08-25 19:30:00', 2),\n(1785, 7, 15, '2012-08-25 08:30:00', 2),\n(1786, 7, 2, '2012-08-25 09:30:00', 2),\n(1787, 7, 4, '2012-08-25 11:00:00', 2),\n(1788, 7, 13, '2012-08-25 14:00:00', 2),\n(1789, 7, 8, '2012-08-25 15:00:00', 2),\n(1790, 7, 0, '2012-08-25 19:00:00', 2),\n(1791, 8, 15, '2012-08-25 08:00:00', 1),\n(1792, 8, 3, '2012-08-25 09:30:00', 3),\n(1793, 8, 16, '2012-08-25 11:00:00', 1),\n(1794, 8, 2, '2012-08-25 12:00:00', 1),\n(1795, 8, 16, '2012-08-25 12:30:00', 2),\n(1796, 8, 3, '2012-08-25 13:30:00', 1),\n(1797, 8, 16, '2012-08-25 14:30:00', 1),\n(1798, 8, 6, '2012-08-25 15:00:00', 1),\n(1799, 8, 3, '2012-08-25 15:30:00', 3),\n(1800, 8, 2, '2012-08-25 17:30:00', 1),\n(1801, 8, 16, '2012-08-25 19:00:00', 1),\n(1802, 0, 11, '2012-08-26 08:30:00', 3),\n(1803, 0, 6, '2012-08-26 10:30:00', 3),\n(1804, 0, 11, '2012-08-26 12:00:00', 3),\n(1805, 0, 0, '2012-08-26 15:00:00', 3),\n(1806, 0, 6, '2012-08-26 17:00:00', 3),\n(1807, 0, 5, '2012-08-26 19:00:00', 3),\n(1808, 1, 12, '2012-08-26 08:30:00', 3),\n(1809, 1, 11, '2012-08-26 10:30:00', 3),\n(1810, 1, 0, '2012-08-26 13:00:00', 6),\n(1811, 1, 13, '2012-08-26 16:00:00', 3),\n(1812, 1, 0, '2012-08-26 17:30:00', 3),\n(1813, 2, 1, '2012-08-26 08:30:00', 3),\n(1814, 2, 16, '2012-08-26 10:00:00', 3),\n(1815, 2, 1, '2012-08-26 11:30:00', 3),\n(1816, 2, 1, '2012-08-26 15:30:00', 3),\n(1817, 2, 0, '2012-08-26 17:30:00', 3),\n(1818, 3, 3, '2012-08-26 08:00:00', 2),\n(1819, 3, 13, '2012-08-26 13:00:00', 2),\n(1820, 3, 10, '2012-08-26 16:00:00', 2),\n(1821, 3, 0, '2012-08-26 18:00:00', 2),\n(1822, 3, 6, '2012-08-26 19:30:00', 2),\n(1823, 4, 0, '2012-08-26 08:00:00', 4),\n(1824, 4, 14, '2012-08-26 10:00:00', 2),\n(1825, 4, 0, '2012-08-26 11:30:00', 2),\n(1826, 4, 10, '2012-08-26 13:00:00', 2),\n(1827, 4, 0, '2012-08-26 15:30:00', 2),\n(1828, 4, 3, '2012-08-26 18:30:00', 2),\n(1829, 5, 0, '2012-08-26 08:00:00', 2),\n(1830, 6, 0, '2012-08-26 08:00:00', 2),\n(1831, 6, 0, '2012-08-26 11:00:00', 2),\n(1832, 6, 12, '2012-08-26 12:00:00', 2),\n(1833, 6, 0, '2012-08-26 15:00:00', 2),\n(1834, 6, 12, '2012-08-26 16:00:00', 2),\n(1835, 6, 0, '2012-08-26 18:30:00', 4),\n(1836, 7, 4, '2012-08-26 08:00:00', 2),\n(1837, 7, 17, '2012-08-26 10:00:00', 2),\n(1838, 7, 8, '2012-08-26 11:30:00', 2),\n(1839, 7, 4, '2012-08-26 13:30:00', 2),\n(1840, 7, 7, '2012-08-26 16:30:00', 2),\n(1841, 7, 7, '2012-08-26 18:00:00', 2),\n(1842, 7, 0, '2012-08-26 19:00:00', 2),\n(1843, 8, 15, '2012-08-26 08:00:00', 1),\n(1844, 8, 3, '2012-08-26 09:30:00', 1),\n(1845, 8, 3, '2012-08-26 10:30:00', 2),\n(1846, 8, 16, '2012-08-26 11:30:00', 1),\n(1847, 8, 3, '2012-08-26 12:00:00', 1),\n(1848, 8, 15, '2012-08-26 12:30:00', 1),\n(1849, 8, 3, '2012-08-26 14:00:00', 1),\n(1850, 8, 16, '2012-08-26 14:30:00', 1),\n(1851, 8, 3, '2012-08-26 15:30:00', 1),\n(1852, 8, 0, '2012-08-26 16:00:00', 1),\n(1853, 8, 0, '2012-08-26 17:00:00', 1),\n(1854, 8, 3, '2012-08-26 18:00:00', 1),\n(1855, 8, 8, '2012-08-26 20:00:00', 1),\n(1856, 0, 0, '2012-08-27 09:00:00', 3),\n(1857, 0, 5, '2012-08-27 10:30:00', 3),\n(1858, 0, 17, '2012-08-27 13:00:00', 3),\n(1859, 0, 7, '2012-08-27 15:30:00', 3),\n(1860, 0, 0, '2012-08-27 17:30:00', 6),\n(1861, 1, 12, '2012-08-27 08:30:00', 3),\n(1862, 1, 0, '2012-08-27 11:00:00', 3),\n(1863, 1, 9, '2012-08-27 12:30:00', 3),\n(1864, 1, 8, '2012-08-27 14:30:00', 3),\n(1865, 1, 9, '2012-08-27 16:30:00', 3),\n(1866, 1, 10, '2012-08-27 18:30:00', 3),\n(1867, 2, 0, '2012-08-27 08:00:00', 3),\n(1868, 2, 0, '2012-08-27 11:00:00', 3),\n(1869, 2, 2, '2012-08-27 14:30:00', 3),\n(1870, 2, 2, '2012-08-27 16:30:00', 3),\n(1871, 3, 15, '2012-08-27 09:30:00', 2),\n(1872, 3, 0, '2012-08-27 11:30:00', 2),\n(1873, 3, 11, '2012-08-27 14:00:00', 2),\n(1874, 3, 16, '2012-08-27 17:00:00', 2),\n(1875, 3, 16, '2012-08-27 19:30:00', 2),\n(1876, 4, 9, '2012-08-27 08:30:00', 2),\n(1877, 4, 5, '2012-08-27 09:30:00', 2),\n(1878, 4, 3, '2012-08-27 10:30:00', 2),\n(1879, 4, 0, '2012-08-27 12:00:00', 2),\n(1880, 4, 8, '2012-08-27 13:30:00', 2),\n(1881, 4, 13, '2012-08-27 14:30:00', 2),\n(1882, 4, 11, '2012-08-27 15:30:00', 2),\n(1883, 4, 0, '2012-08-27 16:30:00', 2),\n(1884, 4, 11, '2012-08-27 18:00:00', 2),\n(1885, 4, 12, '2012-08-27 19:00:00', 2),\n(1886, 5, 20, '2012-08-27 09:00:00', 2),\n(1887, 5, 0, '2012-08-27 10:30:00', 2),\n(1888, 5, 12, '2012-08-27 14:00:00', 2),\n(1889, 6, 0, '2012-08-27 08:00:00', 2),\n(1890, 6, 0, '2012-08-27 09:30:00', 4),\n(1891, 6, 6, '2012-08-27 13:00:00', 2),\n(1892, 6, 0, '2012-08-27 15:00:00', 2),\n(1893, 6, 12, '2012-08-27 16:30:00', 2),\n(1894, 6, 0, '2012-08-27 17:30:00', 2),\n(1895, 6, 1, '2012-08-27 19:00:00', 2),\n(1896, 7, 17, '2012-08-27 09:00:00', 2),\n(1897, 7, 4, '2012-08-27 10:30:00', 2),\n(1898, 7, 2, '2012-08-27 12:30:00', 2),\n(1899, 7, 14, '2012-08-27 13:30:00', 2),\n(1900, 7, 4, '2012-08-27 14:30:00', 2),\n(1901, 7, 13, '2012-08-27 17:00:00', 2),\n(1902, 7, 8, '2012-08-27 18:00:00', 2),\n(1903, 7, 15, '2012-08-27 19:00:00', 2),\n(1904, 8, 9, '2012-08-27 08:00:00', 1),\n(1905, 8, 16, '2012-08-27 10:00:00', 1),\n(1906, 8, 16, '2012-08-27 11:00:00', 3),\n(1907, 8, 3, '2012-08-27 13:30:00', 1),\n(1908, 8, 9, '2012-08-27 14:30:00', 1),\n(1909, 8, 16, '2012-08-27 15:00:00', 1),\n(1910, 8, 4, '2012-08-27 15:30:00', 1),\n(1911, 8, 12, '2012-08-27 16:00:00', 1),\n(1912, 8, 11, '2012-08-27 17:00:00', 1),\n(1913, 8, 3, '2012-08-27 20:00:00', 1),\n(1914, 0, 11, '2012-08-28 08:30:00', 3),\n(1915, 0, 14, '2012-08-28 10:00:00', 3),\n(1916, 0, 10, '2012-08-28 11:30:00', 3),\n(1917, 0, 17, '2012-08-28 14:30:00', 3),\n(1918, 0, 6, '2012-08-28 16:00:00', 3),\n(1919, 0, 16, '2012-08-28 17:30:00', 3),\n(1920, 1, 12, '2012-08-28 08:30:00', 3),\n(1921, 1, 11, '2012-08-28 13:00:00', 3),\n(1922, 1, 9, '2012-08-28 14:30:00', 3),\n(1923, 1, 12, '2012-08-28 19:00:00', 3),\n(1924, 2, 17, '2012-08-28 08:30:00', 3),\n(1925, 2, 1, '2012-08-28 10:30:00', 3),\n(1926, 2, 2, '2012-08-28 12:00:00', 3),\n(1927, 2, 1, '2012-08-28 13:30:00', 9),\n(1928, 2, 0, '2012-08-28 18:00:00', 3),\n(1929, 3, 8, '2012-08-28 11:30:00', 2),\n(1930, 3, 15, '2012-08-28 13:00:00', 2),\n(1931, 3, 20, '2012-08-28 14:00:00', 2),\n(1932, 3, 17, '2012-08-28 18:30:00', 2),\n(1933, 4, 8, '2012-08-28 08:30:00', 2),\n(1934, 4, 3, '2012-08-28 10:30:00', 2),\n(1935, 4, 0, '2012-08-28 11:30:00', 4),\n(1936, 4, 17, '2012-08-28 13:30:00', 2),\n(1937, 4, 10, '2012-08-28 15:30:00', 2),\n(1938, 4, 0, '2012-08-28 16:30:00', 2),\n(1939, 4, 13, '2012-08-28 18:30:00', 2),\n(1940, 4, 20, '2012-08-28 19:30:00', 2),\n(1941, 5, 0, '2012-08-28 09:00:00', 2),\n(1942, 5, 7, '2012-08-28 10:30:00', 2),\n(1943, 5, 0, '2012-08-28 16:00:00', 2),\n(1944, 5, 0, '2012-08-28 18:00:00', 2),\n(1945, 6, 6, '2012-08-28 08:00:00', 2),\n(1946, 6, 0, '2012-08-28 10:30:00', 4),\n(1947, 6, 14, '2012-08-28 12:30:00', 2),\n(1948, 6, 12, '2012-08-28 18:00:00', 2),\n(1949, 7, 13, '2012-08-28 08:00:00', 2),\n(1950, 7, 2, '2012-08-28 09:00:00', 2),\n(1951, 7, 8, '2012-08-28 10:00:00', 2),\n(1952, 7, 9, '2012-08-28 13:30:00', 2),\n(1953, 7, 15, '2012-08-28 14:30:00', 2),\n(1954, 7, 4, '2012-08-28 17:00:00', 2),\n(1955, 7, 2, '2012-08-28 18:00:00', 2),\n(1956, 7, 4, '2012-08-28 19:00:00', 2),\n(1957, 8, 16, '2012-08-28 08:00:00', 1),\n(1958, 8, 3, '2012-08-28 09:30:00', 1),\n(1959, 8, 16, '2012-08-28 10:00:00', 1),\n(1960, 8, 2, '2012-08-28 11:30:00', 1),\n(1961, 8, 12, '2012-08-28 12:00:00', 1),\n(1962, 8, 16, '2012-08-28 13:00:00', 1),\n(1963, 8, 4, '2012-08-28 13:30:00', 1),\n(1964, 8, 16, '2012-08-28 15:30:00', 1),\n(1965, 8, 3, '2012-08-28 17:00:00', 2),\n(1966, 8, 0, '2012-08-28 19:00:00', 1),\n(1967, 0, 0, '2012-08-29 08:30:00', 3),\n(1968, 0, 7, '2012-08-29 11:30:00', 3),\n(1969, 0, 10, '2012-08-29 13:30:00', 3),\n(1970, 0, 0, '2012-08-29 16:00:00', 3),\n(1971, 0, 9, '2012-08-29 17:30:00', 3),\n(1972, 0, 0, '2012-08-29 19:00:00', 3),\n(1973, 1, 12, '2012-08-29 08:00:00', 3),\n(1974, 1, 10, '2012-08-29 10:00:00', 3),\n(1975, 1, 9, '2012-08-29 13:30:00', 3),\n(1976, 1, 10, '2012-08-29 16:30:00', 3),\n(1977, 1, 8, '2012-08-29 18:00:00', 3),\n(1978, 2, 1, '2012-08-29 08:30:00', 3),\n(1979, 2, 1, '2012-08-29 10:30:00', 3),\n(1980, 2, 8, '2012-08-29 12:00:00', 3),\n(1981, 2, 8, '2012-08-29 14:00:00', 3),\n(1982, 2, 6, '2012-08-29 15:30:00', 3),\n(1983, 2, 1, '2012-08-29 17:30:00', 3),\n(1984, 2, 11, '2012-08-29 19:00:00', 3),\n(1985, 3, 3, '2012-08-29 08:30:00', 2),\n(1986, 3, 3, '2012-08-29 10:30:00', 2),\n(1987, 3, 1, '2012-08-29 14:00:00', 2),\n(1988, 3, 14, '2012-08-29 16:00:00', 2),\n(1989, 3, 16, '2012-08-29 17:00:00', 2),\n(1990, 3, 3, '2012-08-29 18:30:00', 4),\n(1991, 4, 0, '2012-08-29 08:30:00', 2),\n(1992, 4, 13, '2012-08-29 10:00:00', 2),\n(1993, 4, 0, '2012-08-29 11:00:00', 2),\n(1994, 4, 1, '2012-08-29 12:00:00', 2),\n(1995, 4, 0, '2012-08-29 13:00:00', 4),\n(1996, 4, 5, '2012-08-29 15:00:00', 2),\n(1997, 4, 0, '2012-08-29 16:30:00', 2),\n(1998, 4, 14, '2012-08-29 18:00:00', 2),\n(1999, 4, 20, '2012-08-29 19:30:00', 2),\n(2000, 6, 0, '2012-08-29 08:00:00', 2),\n(2001, 6, 6, '2012-08-29 10:30:00', 4),\n(2002, 6, 0, '2012-08-29 13:00:00', 2),\n(2003, 6, 0, '2012-08-29 15:30:00', 2),\n(2004, 6, 12, '2012-08-29 17:30:00', 2),\n(2005, 6, 12, '2012-08-29 19:00:00', 2),\n(2006, 7, 8, '2012-08-29 10:00:00', 2),\n(2007, 7, 15, '2012-08-29 13:00:00', 2),\n(2008, 7, 4, '2012-08-29 15:00:00', 2),\n(2009, 7, 2, '2012-08-29 16:30:00', 2),\n(2010, 7, 13, '2012-08-29 17:30:00', 2),\n(2011, 7, 4, '2012-08-29 18:30:00', 2),\n(2012, 7, 8, '2012-08-29 19:30:00', 2),\n(2013, 8, 15, '2012-08-29 08:00:00', 1),\n(2014, 8, 0, '2012-08-29 11:30:00', 1),\n(2015, 8, 3, '2012-08-29 13:30:00', 1),\n(2016, 8, 16, '2012-08-29 14:00:00', 1),\n(2017, 8, 3, '2012-08-29 15:00:00', 2),\n(2018, 8, 0, '2012-08-29 17:30:00', 1),\n(2019, 8, 16, '2012-08-29 18:30:00', 1),\n(2020, 8, 1, '2012-08-29 19:30:00', 1),\n(2021, 0, 0, '2012-08-30 08:00:00', 3),\n(2022, 0, 17, '2012-08-30 09:30:00', 3),\n(2023, 0, 5, '2012-08-30 12:30:00', 3),\n(2024, 0, 0, '2012-08-30 14:00:00', 3),\n(2025, 0, 5, '2012-08-30 16:00:00', 3),\n(2026, 1, 8, '2012-08-30 08:00:00', 3),\n(2027, 1, 10, '2012-08-30 12:30:00', 3),\n(2028, 1, 11, '2012-08-30 14:00:00', 3),\n(2029, 1, 0, '2012-08-30 16:00:00', 3),\n(2030, 1, 0, '2012-08-30 19:00:00', 3),\n(2031, 2, 1, '2012-08-30 11:00:00', 3),\n(2032, 2, 15, '2012-08-30 12:30:00', 3),\n(2033, 2, 1, '2012-08-30 14:00:00', 3),\n(2034, 2, 7, '2012-08-30 17:00:00', 3),\n(2035, 2, 21, '2012-08-30 19:00:00', 3),\n(2036, 3, 10, '2012-08-30 08:00:00', 2),\n(2037, 3, 6, '2012-08-30 09:30:00', 2),\n(2038, 3, 14, '2012-08-30 12:30:00', 2),\n(2039, 3, 20, '2012-08-30 15:00:00', 2),\n(2040, 3, 20, '2012-08-30 16:30:00', 2),\n(2041, 3, 16, '2012-08-30 17:30:00', 2),\n(2042, 3, 6, '2012-08-30 19:30:00', 2),\n(2043, 4, 0, '2012-08-30 08:00:00', 2),\n(2044, 4, 13, '2012-08-30 09:00:00', 2),\n(2045, 4, 0, '2012-08-30 10:00:00', 2),\n(2046, 4, 10, '2012-08-30 14:30:00', 2),\n(2047, 4, 11, '2012-08-30 15:30:00', 2),\n(2048, 4, 1, '2012-08-30 16:30:00', 2),\n(2049, 4, 0, '2012-08-30 18:30:00', 2),\n(2050, 6, 12, '2012-08-30 08:00:00', 6),\n(2051, 6, 12, '2012-08-30 11:30:00', 2),\n(2052, 6, 0, '2012-08-30 13:00:00', 4),\n(2053, 6, 0, '2012-08-30 15:30:00', 2),\n(2054, 6, 12, '2012-08-30 16:30:00', 2),\n(2055, 6, 0, '2012-08-30 17:30:00', 2),\n(2056, 7, 0, '2012-08-30 11:30:00', 2),\n(2057, 7, 4, '2012-08-30 14:30:00', 2),\n(2058, 7, 15, '2012-08-30 17:30:00', 2),\n(2059, 7, 8, '2012-08-30 19:00:00', 2),\n(2060, 8, 1, '2012-08-30 08:00:00', 1),\n(2061, 8, 21, '2012-08-30 10:00:00', 1),\n(2062, 8, 3, '2012-08-30 10:30:00', 1),\n(2063, 8, 20, '2012-08-30 11:00:00', 1),\n(2064, 8, 17, '2012-08-30 12:30:00', 1),\n(2065, 8, 3, '2012-08-30 13:00:00', 1),\n(2066, 8, 2, '2012-08-30 14:00:00', 1),\n(2067, 8, 21, '2012-08-30 15:30:00', 3),\n(2068, 8, 3, '2012-08-30 18:00:00', 1),\n(2069, 8, 6, '2012-08-30 19:00:00', 1),\n(2070, 8, 16, '2012-08-30 19:30:00', 1),\n(2071, 8, 9, '2012-08-30 20:00:00', 1),\n(2072, 0, 5, '2012-08-31 09:00:00', 3),\n(2073, 0, 0, '2012-08-31 10:30:00', 3),\n(2074, 0, 11, '2012-08-31 12:00:00', 3),\n(2075, 0, 6, '2012-08-31 14:30:00', 3),\n(2076, 0, 2, '2012-08-31 16:30:00', 3),\n(2077, 0, 5, '2012-08-31 19:00:00', 3),\n(2078, 1, 0, '2012-08-31 08:00:00', 3),\n(2079, 1, 0, '2012-08-31 10:30:00', 3),\n(2080, 1, 12, '2012-08-31 12:00:00', 3),\n(2081, 1, 8, '2012-08-31 13:30:00', 3),\n(2082, 1, 10, '2012-08-31 15:00:00', 6),\n(2083, 1, 8, '2012-08-31 18:30:00', 3),\n(2084, 2, 2, '2012-08-31 08:30:00', 3),\n(2085, 2, 0, '2012-08-31 11:00:00', 3),\n(2086, 2, 16, '2012-08-31 12:30:00', 3),\n(2087, 2, 21, '2012-08-31 14:00:00', 3),\n(2088, 2, 21, '2012-08-31 17:00:00', 3),\n(2089, 2, 0, '2012-08-31 19:00:00', 3),\n(2090, 3, 20, '2012-08-31 09:00:00', 2),\n(2091, 3, 10, '2012-08-31 10:30:00', 2),\n(2092, 3, 3, '2012-08-31 12:30:00', 2),\n(2093, 3, 20, '2012-08-31 19:30:00', 2),\n(2094, 4, 0, '2012-08-31 08:30:00', 2),\n(2095, 4, 0, '2012-08-31 10:00:00', 2),\n(2096, 4, 14, '2012-08-31 12:30:00', 2),\n(2097, 4, 0, '2012-08-31 13:30:00', 2),\n(2098, 4, 11, '2012-08-31 14:30:00', 4),\n(2099, 4, 9, '2012-08-31 16:30:00', 2),\n(2100, 4, 6, '2012-08-31 18:00:00', 2),\n(2101, 4, 11, '2012-08-31 19:00:00', 2),\n(2102, 5, 0, '2012-08-31 09:30:00', 2),\n(2103, 5, 0, '2012-08-31 11:00:00', 2),\n(2104, 5, 0, '2012-08-31 15:00:00', 2),\n(2105, 5, 11, '2012-08-31 17:00:00', 2),\n(2106, 6, 1, '2012-08-31 09:00:00', 4),\n(2107, 6, 0, '2012-08-31 11:00:00', 4),\n(2108, 6, 0, '2012-08-31 14:30:00', 4),\n(2109, 6, 12, '2012-08-31 18:00:00', 4),\n(2110, 7, 9, '2012-08-31 08:00:00', 2),\n(2111, 7, 5, '2012-08-31 11:30:00', 2),\n(2112, 7, 17, '2012-08-31 13:00:00', 2),\n(2113, 7, 15, '2012-08-31 15:00:00', 2),\n(2114, 7, 17, '2012-08-31 16:30:00', 2),\n(2115, 7, 13, '2012-08-31 17:30:00', 2),\n(2116, 7, 10, '2012-08-31 18:30:00', 2),\n(2117, 8, 17, '2012-08-31 08:30:00', 1),\n(2118, 8, 3, '2012-08-31 10:00:00', 1),\n(2119, 8, 21, '2012-08-31 12:30:00', 2),\n(2120, 8, 3, '2012-08-31 13:30:00', 1),\n(2121, 8, 15, '2012-08-31 14:00:00', 1),\n(2122, 8, 3, '2012-08-31 14:30:00', 1),\n(2123, 8, 16, '2012-08-31 16:00:00', 1),\n(2124, 8, 6, '2012-08-31 16:30:00', 1),\n(2125, 8, 3, '2012-08-31 17:00:00', 1),\n(2126, 8, 2, '2012-08-31 18:00:00', 1),\n(2127, 8, 20, '2012-08-31 18:30:00', 1),\n(2128, 8, 21, '2012-08-31 19:00:00', 1),\n(2129, 8, 21, '2012-08-31 20:00:00', 1),\n(2130, 0, 0, '2012-09-01 08:00:00', 3),\n(2131, 0, 17, '2012-09-01 11:00:00', 3),\n(2132, 0, 7, '2012-09-01 12:30:00', 3),\n(2133, 0, 6, '2012-09-01 15:00:00', 3),\n(2134, 0, 4, '2012-09-01 17:00:00', 3),\n(2135, 1, 0, '2012-09-01 08:00:00', 3),\n(2136, 1, 11, '2012-09-01 09:30:00', 3),\n(2137, 1, 10, '2012-09-01 11:00:00', 3),\n(2138, 1, 12, '2012-09-01 14:30:00', 3),\n(2139, 1, 0, '2012-09-01 16:30:00', 3),\n(2140, 1, 12, '2012-09-01 19:00:00', 3),\n(2141, 2, 1, '2012-09-01 09:00:00', 3),\n(2142, 2, 21, '2012-09-01 13:30:00', 3),\n(2143, 2, 1, '2012-09-01 16:30:00', 3),\n(2144, 2, 15, '2012-09-01 18:00:00', 3),\n(2145, 3, 17, '2012-09-01 08:30:00', 2),\n(2146, 3, 13, '2012-09-01 09:30:00', 2),\n(2147, 3, 15, '2012-09-01 10:30:00', 2),\n(2148, 3, 17, '2012-09-01 12:30:00', 2),\n(2149, 3, 17, '2012-09-01 14:00:00', 2),\n(2150, 3, 16, '2012-09-01 15:00:00', 2),\n(2151, 3, 0, '2012-09-01 16:30:00', 2),\n(2152, 3, 16, '2012-09-01 18:00:00', 2),\n(2153, 3, 17, '2012-09-01 19:00:00', 2),\n(2154, 4, 8, '2012-09-01 08:30:00', 2),\n(2155, 4, 9, '2012-09-01 11:00:00', 2),\n(2156, 4, 11, '2012-09-01 12:30:00', 2),\n(2157, 4, 0, '2012-09-01 13:30:00', 6),\n(2158, 4, 0, '2012-09-01 17:30:00', 2),\n(2159, 4, 16, '2012-09-01 19:30:00', 2),\n(2160, 5, 0, '2012-09-01 09:30:00', 2),\n(2161, 5, 7, '2012-09-01 15:30:00', 2),\n(2162, 6, 0, '2012-09-01 09:30:00', 8),\n(2163, 6, 4, '2012-09-01 15:00:00', 2),\n(2164, 6, 0, '2012-09-01 16:00:00', 4),\n(2165, 6, 2, '2012-09-01 18:00:00', 2),\n(2166, 7, 21, '2012-09-01 08:30:00', 2),\n(2167, 7, 2, '2012-09-01 11:30:00', 2),\n(2168, 7, 1, '2012-09-01 14:00:00', 2),\n(2169, 7, 15, '2012-09-01 15:00:00', 2),\n(2170, 7, 13, '2012-09-01 17:30:00', 2),\n(2171, 7, 9, '2012-09-01 19:00:00', 2),\n(2172, 8, 17, '2012-09-01 10:00:00', 1),\n(2173, 8, 1, '2012-09-01 10:30:00', 1),\n(2174, 8, 14, '2012-09-01 11:00:00', 1),\n(2175, 8, 21, '2012-09-01 11:30:00', 1),\n(2176, 8, 21, '2012-09-01 15:00:00', 1),\n(2177, 8, 3, '2012-09-01 16:00:00', 1),\n(2178, 8, 20, '2012-09-01 18:00:00', 1),\n(2179, 8, 3, '2012-09-01 18:30:00', 1),\n(2180, 8, 7, '2012-09-01 19:30:00', 1),\n(2181, 0, 10, '2012-09-02 08:30:00', 3),\n(2182, 0, 0, '2012-09-02 10:30:00', 3),\n(2183, 0, 12, '2012-09-02 12:00:00', 3),\n(2184, 0, 5, '2012-09-02 15:00:00', 3),\n(2185, 0, 6, '2012-09-02 18:00:00', 3),\n(2186, 1, 15, '2012-09-02 08:30:00', 3),\n(2187, 1, 11, '2012-09-02 12:30:00', 3),\n(2188, 1, 10, '2012-09-02 16:00:00', 6),\n(2189, 1, 0, '2012-09-02 19:00:00', 3),\n(2190, 2, 0, '2012-09-02 09:30:00', 3),\n(2191, 2, 21, '2012-09-02 11:00:00', 3),\n(2192, 2, 0, '2012-09-02 12:30:00', 3),\n(2193, 2, 9, '2012-09-02 15:30:00', 3),\n(2194, 2, 5, '2012-09-02 17:00:00', 3),\n(2195, 2, 0, '2012-09-02 19:00:00', 3),\n(2196, 3, 15, '2012-09-02 13:30:00', 2),\n(2197, 3, 3, '2012-09-02 14:30:00', 2),\n(2198, 3, 15, '2012-09-02 16:30:00', 2),\n(2199, 3, 15, '2012-09-02 18:00:00', 2),\n(2200, 3, 17, '2012-09-02 19:30:00', 2),\n(2201, 4, 0, '2012-09-02 08:00:00', 2),\n(2202, 4, 0, '2012-09-02 09:30:00', 6),\n(2203, 4, 5, '2012-09-02 12:30:00', 2),\n(2204, 4, 0, '2012-09-02 13:30:00', 4),\n(2205, 4, 20, '2012-09-02 15:30:00', 2),\n(2206, 4, 8, '2012-09-02 16:30:00', 2),\n(2207, 4, 14, '2012-09-02 17:30:00', 2),\n(2208, 4, 0, '2012-09-02 18:30:00', 2),\n(2209, 5, 0, '2012-09-02 09:30:00', 2),\n(2210, 5, 0, '2012-09-02 11:30:00', 2),\n(2211, 6, 0, '2012-09-02 08:30:00', 4),\n(2212, 6, 0, '2012-09-02 11:00:00', 2),\n(2213, 6, 10, '2012-09-02 14:00:00', 2),\n(2214, 6, 0, '2012-09-02 15:00:00', 4),\n(2215, 6, 0, '2012-09-02 17:30:00', 2),\n(2216, 6, 0, '2012-09-02 19:00:00', 2),\n(2217, 7, 17, '2012-09-02 08:30:00', 2),\n(2218, 7, 2, '2012-09-02 10:30:00', 2),\n(2219, 7, 22, '2012-09-02 11:30:00', 2),\n(2220, 7, 7, '2012-09-02 13:00:00', 2),\n(2221, 7, 8, '2012-09-02 14:30:00', 2),\n(2222, 7, 2, '2012-09-02 16:30:00', 2),\n(2223, 7, 2, '2012-09-02 18:30:00', 2),\n(2224, 8, 20, '2012-09-02 08:00:00', 1),\n(2225, 8, 3, '2012-09-02 08:30:00', 1),\n(2226, 8, 16, '2012-09-02 09:30:00', 2),\n(2227, 8, 3, '2012-09-02 10:30:00', 1),\n(2228, 8, 3, '2012-09-02 11:30:00', 1),\n(2229, 8, 7, '2012-09-02 12:30:00', 1),\n(2230, 8, 16, '2012-09-02 13:00:00', 1),\n(2231, 8, 16, '2012-09-02 16:00:00', 1),\n(2232, 8, 3, '2012-09-02 17:30:00', 1),\n(2233, 8, 21, '2012-09-02 18:30:00', 1),\n(2234, 8, 3, '2012-09-02 19:00:00', 1),\n(2235, 8, 16, '2012-09-02 20:00:00', 1),\n(2236, 0, 0, '2012-09-03 08:00:00', 6),\n(2237, 0, 11, '2012-09-03 11:00:00', 6),\n(2238, 0, 14, '2012-09-03 14:00:00', 3),\n(2239, 0, 0, '2012-09-03 15:30:00', 3),\n(2240, 0, 16, '2012-09-03 18:00:00', 3),\n(2241, 1, 12, '2012-09-03 08:00:00', 3),\n(2242, 1, 0, '2012-09-03 10:00:00', 6),\n(2243, 1, 0, '2012-09-03 13:30:00', 3),\n(2244, 1, 8, '2012-09-03 15:00:00', 6),\n(2245, 1, 11, '2012-09-03 18:00:00', 3),\n(2246, 2, 21, '2012-09-03 08:30:00', 3),\n(2247, 2, 12, '2012-09-03 10:00:00', 3),\n(2248, 2, 9, '2012-09-03 12:30:00', 3),\n(2249, 2, 17, '2012-09-03 14:00:00', 3),\n(2250, 2, 0, '2012-09-03 19:00:00', 3),\n(2251, 3, 22, '2012-09-03 09:30:00', 2),\n(2252, 3, 21, '2012-09-03 11:30:00', 2),\n(2253, 3, 13, '2012-09-03 12:30:00', 2),\n(2254, 3, 20, '2012-09-03 13:30:00', 4),\n(2255, 3, 17, '2012-09-03 17:30:00', 2),\n(2256, 3, 20, '2012-09-03 19:00:00', 2),\n(2257, 4, 0, '2012-09-03 08:00:00', 2),\n(2258, 4, 8, '2012-09-03 09:30:00', 2),\n(2259, 4, 0, '2012-09-03 11:00:00', 4),\n(2260, 4, 8, '2012-09-03 13:00:00', 2),\n(2261, 4, 0, '2012-09-03 15:00:00', 2),\n(2262, 4, 3, '2012-09-03 16:00:00', 2),\n(2263, 4, 0, '2012-09-03 17:00:00', 2),\n(2264, 4, 14, '2012-09-03 19:00:00', 2),\n(2265, 5, 10, '2012-09-03 11:30:00', 2),\n(2266, 6, 6, '2012-09-03 11:00:00', 2),\n(2267, 6, 0, '2012-09-03 12:00:00', 2),\n(2268, 6, 0, '2012-09-03 13:30:00', 4),\n(2269, 6, 6, '2012-09-03 16:00:00', 4),\n(2270, 6, 12, '2012-09-03 18:30:00', 2),\n(2271, 6, 0, '2012-09-03 19:30:00', 2),\n(2272, 7, 15, '2012-09-03 09:30:00', 2),\n(2273, 7, 4, '2012-09-03 12:00:00', 4),\n(2274, 7, 15, '2012-09-03 15:00:00', 2),\n(2275, 7, 15, '2012-09-03 17:00:00', 2),\n(2276, 7, 1, '2012-09-03 18:00:00', 2),\n(2277, 7, 7, '2012-09-03 19:00:00', 2),\n(2278, 8, 2, '2012-09-03 08:00:00', 1),\n(2279, 8, 7, '2012-09-03 08:30:00', 1),\n(2280, 8, 16, '2012-09-03 10:00:00', 1),\n(2281, 8, 1, '2012-09-03 10:30:00', 1),\n(2282, 8, 0, '2012-09-03 11:30:00', 1),\n(2283, 8, 3, '2012-09-03 13:00:00', 1),\n(2284, 8, 21, '2012-09-03 14:00:00', 1),\n(2285, 8, 3, '2012-09-03 15:00:00', 1),\n(2286, 8, 21, '2012-09-03 15:30:00', 1),\n(2287, 8, 21, '2012-09-03 17:00:00', 1),\n(2288, 8, 16, '2012-09-03 17:30:00', 1),\n(2289, 8, 20, '2012-09-03 18:30:00', 1),\n(2290, 8, 21, '2012-09-03 20:00:00', 1),\n(2291, 0, 11, '2012-09-04 08:30:00', 3),\n(2292, 0, 0, '2012-09-04 10:00:00', 3),\n(2293, 0, 10, '2012-09-04 11:30:00', 3),\n(2294, 0, 0, '2012-09-04 13:30:00', 3),\n(2295, 0, 5, '2012-09-04 15:00:00', 3),\n(2296, 0, 0, '2012-09-04 16:30:00', 3),\n(2297, 1, 0, '2012-09-04 10:00:00', 3),\n(2298, 1, 8, '2012-09-04 12:00:00', 3),\n(2299, 1, 0, '2012-09-04 14:00:00', 3),\n(2300, 1, 0, '2012-09-04 16:00:00', 3),\n(2301, 1, 9, '2012-09-04 17:30:00', 3),\n(2302, 1, 24, '2012-09-04 19:00:00', 3),\n(2303, 2, 21, '2012-09-04 08:00:00', 3),\n(2304, 2, 14, '2012-09-04 09:30:00', 3),\n(2305, 2, 15, '2012-09-04 11:00:00', 3),\n(2306, 2, 0, '2012-09-04 12:30:00', 3),\n(2307, 2, 0, '2012-09-04 15:00:00', 3),\n(2308, 2, 5, '2012-09-04 16:30:00', 3),\n(2309, 2, 2, '2012-09-04 18:00:00', 3),\n(2310, 3, 20, '2012-09-04 10:30:00', 2),\n(2311, 3, 21, '2012-09-04 11:30:00', 2),\n(2312, 3, 17, '2012-09-04 13:30:00', 2),\n(2313, 3, 21, '2012-09-04 15:00:00', 2),\n(2314, 3, 20, '2012-09-04 17:30:00', 2),\n(2315, 3, 22, '2012-09-04 18:30:00', 2),\n(2316, 4, 0, '2012-09-04 08:00:00', 2),\n(2317, 4, 3, '2012-09-04 10:30:00', 2),\n(2318, 4, 0, '2012-09-04 11:30:00', 2),\n(2319, 4, 7, '2012-09-04 12:30:00', 2),\n(2320, 4, 0, '2012-09-04 13:30:00', 2),\n(2321, 4, 3, '2012-09-04 15:00:00', 2),\n(2322, 4, 0, '2012-09-04 16:00:00', 2),\n(2323, 4, 0, '2012-09-04 17:30:00', 2),\n(2324, 4, 11, '2012-09-04 18:30:00', 2),\n(2325, 4, 8, '2012-09-04 19:30:00', 2),\n(2326, 5, 0, '2012-09-04 09:30:00', 2),\n(2327, 5, 0, '2012-09-04 12:30:00', 2),\n(2328, 6, 0, '2012-09-04 08:00:00', 4),\n(2329, 6, 0, '2012-09-04 11:00:00', 2),\n(2330, 6, 12, '2012-09-04 12:00:00', 2),\n(2331, 6, 0, '2012-09-04 13:30:00', 2),\n(2332, 6, 5, '2012-09-04 18:30:00', 2),\n(2333, 7, 22, '2012-09-04 08:00:00', 2),\n(2334, 7, 8, '2012-09-04 09:00:00', 2),\n(2335, 7, 7, '2012-09-04 10:00:00', 2),\n(2336, 7, 24, '2012-09-04 11:00:00', 2),\n(2337, 7, 5, '2012-09-04 13:00:00', 2),\n(2338, 7, 24, '2012-09-04 16:00:00', 2),\n(2339, 7, 0, '2012-09-04 17:30:00', 2),\n(2340, 7, 14, '2012-09-04 19:00:00', 2),\n(2341, 8, 3, '2012-09-04 08:00:00', 1),\n(2342, 8, 3, '2012-09-04 09:00:00', 1),\n(2343, 8, 20, '2012-09-04 09:30:00', 1),\n(2344, 8, 21, '2012-09-04 10:00:00', 3),\n(2345, 8, 0, '2012-09-04 13:00:00', 1),\n(2346, 8, 21, '2012-09-04 13:30:00', 1),\n(2347, 8, 3, '2012-09-04 14:00:00', 1),\n(2348, 8, 8, '2012-09-04 15:00:00', 2),\n(2349, 8, 21, '2012-09-04 16:00:00', 1),\n(2350, 8, 3, '2012-09-04 18:30:00', 1),\n(2351, 8, 21, '2012-09-04 19:30:00', 1),\n(2352, 8, 16, '2012-09-04 20:00:00', 1),\n(2353, 0, 22, '2012-09-05 08:00:00', 3),\n(2354, 0, 12, '2012-09-05 09:30:00', 3),\n(2355, 0, 0, '2012-09-05 11:00:00', 3),\n(2356, 0, 2, '2012-09-05 14:00:00', 3),\n(2357, 0, 6, '2012-09-05 15:30:00', 3),\n(2358, 0, 17, '2012-09-05 18:00:00', 3),\n(2359, 1, 1, '2012-09-05 08:00:00', 3),\n(2360, 1, 10, '2012-09-05 09:30:00', 3),\n(2361, 1, 24, '2012-09-05 12:00:00', 3),\n(2362, 1, 8, '2012-09-05 15:30:00', 3),\n(2363, 1, 12, '2012-09-05 18:00:00', 3),\n(2364, 2, 7, '2012-09-05 08:30:00', 3),\n(2365, 2, 13, '2012-09-05 11:30:00', 3),\n(2366, 2, 1, '2012-09-05 13:00:00', 3),\n(2367, 2, 24, '2012-09-05 16:30:00', 3),\n(2368, 2, 1, '2012-09-05 18:00:00', 3),\n(2369, 3, 16, '2012-09-05 08:30:00', 2),\n(2370, 3, 15, '2012-09-05 09:30:00', 2),\n(2371, 3, 2, '2012-09-05 12:00:00', 2),\n(2372, 3, 10, '2012-09-05 15:30:00', 2),\n(2373, 3, 10, '2012-09-05 19:30:00', 2),\n(2374, 4, 24, '2012-09-05 08:00:00', 2),\n(2375, 4, 0, '2012-09-05 09:00:00', 4),\n(2376, 4, 0, '2012-09-05 11:30:00', 2),\n(2377, 4, 16, '2012-09-05 12:30:00', 2),\n(2378, 4, 0, '2012-09-05 13:30:00', 6),\n(2379, 4, 11, '2012-09-05 17:00:00', 2),\n(2380, 4, 0, '2012-09-05 18:00:00', 2),\n(2381, 4, 9, '2012-09-05 19:00:00', 2),\n(2382, 5, 0, '2012-09-05 09:00:00', 2),\n(2383, 5, 0, '2012-09-05 11:00:00', 2),\n(2384, 5, 0, '2012-09-05 12:30:00', 2),\n(2385, 6, 0, '2012-09-05 08:30:00', 4),\n(2386, 6, 0, '2012-09-05 11:00:00', 2),\n(2387, 6, 11, '2012-09-05 13:00:00', 2),\n(2388, 6, 0, '2012-09-05 14:00:00', 2),\n(2389, 6, 0, '2012-09-05 15:30:00', 6),\n(2390, 7, 8, '2012-09-05 08:00:00', 2),\n(2391, 7, 4, '2012-09-05 10:00:00', 2),\n(2392, 7, 15, '2012-09-05 11:00:00', 2),\n(2393, 7, 7, '2012-09-05 13:00:00', 2),\n(2394, 7, 4, '2012-09-05 15:00:00', 2),\n(2395, 7, 9, '2012-09-05 16:30:00', 2),\n(2396, 7, 5, '2012-09-05 18:30:00', 2),\n(2397, 8, 20, '2012-09-05 09:00:00', 1),\n(2398, 8, 14, '2012-09-05 10:30:00', 1),\n(2399, 8, 3, '2012-09-05 11:00:00', 2),\n(2400, 8, 20, '2012-09-05 13:00:00', 1),\n(2401, 8, 2, '2012-09-05 13:30:00', 1),\n(2402, 8, 21, '2012-09-05 14:00:00', 1),\n(2403, 8, 0, '2012-09-05 14:30:00', 1),\n(2404, 8, 9, '2012-09-05 15:00:00', 1),\n(2405, 8, 2, '2012-09-05 15:30:00', 1),\n(2406, 8, 16, '2012-09-05 16:00:00', 1),\n(2407, 8, 6, '2012-09-05 17:00:00', 1),\n(2408, 8, 8, '2012-09-05 17:30:00', 1),\n(2409, 8, 2, '2012-09-05 19:00:00', 1),\n(2410, 8, 1, '2012-09-05 20:00:00', 1),\n(2411, 0, 17, '2012-09-06 08:30:00', 3),\n(2412, 0, 11, '2012-09-06 10:30:00', 3),\n(2413, 0, 22, '2012-09-06 12:00:00', 3),\n(2414, 0, 11, '2012-09-06 16:30:00', 3),\n(2415, 0, 4, '2012-09-06 19:00:00', 3),\n(2416, 1, 0, '2012-09-06 08:30:00', 3),\n(2417, 1, 8, '2012-09-06 10:00:00', 3),\n(2418, 1, 0, '2012-09-06 11:30:00', 3),\n(2419, 1, 9, '2012-09-06 13:00:00', 3),\n(2420, 1, 12, '2012-09-06 16:30:00', 6),\n(2421, 2, 9, '2012-09-06 08:00:00', 3),\n(2422, 2, 15, '2012-09-06 09:30:00', 3),\n(2423, 2, 21, '2012-09-06 12:00:00', 3),\n(2424, 2, 12, '2012-09-06 13:30:00', 3),\n(2425, 2, 17, '2012-09-06 15:00:00', 3),\n(2426, 2, 1, '2012-09-06 17:30:00', 3),\n(2427, 2, 21, '2012-09-06 19:00:00', 3),\n(2428, 3, 13, '2012-09-06 08:30:00', 2),\n(2429, 3, 15, '2012-09-06 11:00:00', 2),\n(2430, 3, 17, '2012-09-06 13:00:00', 2),\n(2431, 3, 13, '2012-09-06 14:30:00', 2),\n(2432, 3, 20, '2012-09-06 15:30:00', 2),\n(2433, 3, 15, '2012-09-06 17:30:00', 2),\n(2434, 4, 0, '2012-09-06 08:00:00', 2),\n(2435, 4, 2, '2012-09-06 09:30:00', 2),\n(2436, 4, 24, '2012-09-06 10:30:00', 2),\n(2437, 4, 13, '2012-09-06 12:00:00', 2),\n(2438, 4, 0, '2012-09-06 13:00:00', 2),\n(2439, 4, 6, '2012-09-06 14:00:00', 2),\n(2440, 4, 0, '2012-09-06 15:00:00', 4),\n(2441, 4, 7, '2012-09-06 17:30:00', 2),\n(2442, 4, 16, '2012-09-06 18:30:00', 2),\n(2443, 4, 8, '2012-09-06 19:30:00', 2),\n(2444, 5, 0, '2012-09-06 11:00:00', 2),\n(2445, 6, 0, '2012-09-06 09:00:00', 4),\n(2446, 6, 0, '2012-09-06 11:30:00', 2),\n(2447, 6, 14, '2012-09-06 12:30:00', 2),\n(2448, 6, 0, '2012-09-06 13:30:00', 2),\n(2449, 6, 0, '2012-09-06 15:30:00', 2),\n(2450, 6, 0, '2012-09-06 17:00:00', 2),\n(2451, 6, 0, '2012-09-06 18:30:00', 2),\n(2452, 6, 13, '2012-09-06 19:30:00', 2),\n(2453, 7, 0, '2012-09-06 09:00:00', 2),\n(2454, 7, 15, '2012-09-06 12:30:00', 2),\n(2455, 7, 24, '2012-09-06 16:30:00', 2),\n(2456, 7, 10, '2012-09-06 17:30:00', 2),\n(2457, 7, 0, '2012-09-06 18:30:00', 2),\n(2458, 8, 21, '2012-09-06 09:00:00', 1),\n(2459, 8, 24, '2012-09-06 09:30:00', 1),\n(2460, 8, 16, '2012-09-06 10:00:00', 1),\n(2461, 8, 7, '2012-09-06 11:00:00', 1),\n(2462, 8, 9, '2012-09-06 11:30:00', 1),\n(2463, 8, 3, '2012-09-06 12:00:00', 1),\n(2464, 8, 0, '2012-09-06 13:30:00', 1),\n(2465, 8, 20, '2012-09-06 14:00:00', 1),\n(2466, 8, 24, '2012-09-06 15:00:00', 1),\n(2467, 8, 3, '2012-09-06 16:30:00', 1),\n(2468, 8, 22, '2012-09-06 17:00:00', 1),\n(2469, 8, 16, '2012-09-06 18:00:00', 1),\n(2470, 8, 2, '2012-09-06 19:00:00', 1),\n(2471, 8, 3, '2012-09-06 19:30:00', 2),\n(2472, 0, 0, '2012-09-07 08:00:00', 3),\n(2473, 0, 14, '2012-09-07 09:30:00', 6),\n(2474, 0, 0, '2012-09-07 12:30:00', 3),\n(2475, 0, 11, '2012-09-07 14:00:00', 3),\n(2476, 0, 17, '2012-09-07 16:00:00', 3),\n(2477, 0, 14, '2012-09-07 18:00:00', 3),\n(2478, 1, 9, '2012-09-07 08:00:00', 3),\n(2479, 1, 12, '2012-09-07 11:00:00', 3),\n(2480, 1, 11, '2012-09-07 12:30:00', 3),\n(2481, 1, 24, '2012-09-07 14:30:00', 3),\n(2482, 1, 12, '2012-09-07 16:30:00', 3),\n(2483, 1, 9, '2012-09-07 18:00:00', 3),\n(2484, 2, 1, '2012-09-07 08:00:00', 3),\n(2485, 2, 5, '2012-09-07 09:30:00', 3),\n(2486, 2, 1, '2012-09-07 11:00:00', 3),\n(2487, 2, 0, '2012-09-07 12:30:00', 3),\n(2488, 2, 1, '2012-09-07 14:00:00', 6),\n(2489, 2, 0, '2012-09-07 17:00:00', 3),\n(2490, 2, 1, '2012-09-07 19:00:00', 3),\n(2491, 3, 16, '2012-09-07 08:30:00', 2),\n(2492, 3, 15, '2012-09-07 11:00:00', 2),\n(2493, 3, 20, '2012-09-07 14:30:00', 2),\n(2494, 3, 7, '2012-09-07 17:00:00', 2),\n(2495, 3, 10, '2012-09-07 19:00:00', 2),\n(2496, 4, 3, '2012-09-07 08:30:00', 4),\n(2497, 4, 6, '2012-09-07 11:00:00', 2),\n(2498, 4, 20, '2012-09-07 12:00:00', 2),\n(2499, 4, 5, '2012-09-07 13:00:00', 2),\n(2500, 4, 16, '2012-09-07 14:30:00', 2),\n(2501, 4, 0, '2012-09-07 16:00:00', 2),\n(2502, 4, 10, '2012-09-07 18:00:00', 2),\n(2503, 4, 13, '2012-09-07 19:00:00', 2),\n(2504, 5, 24, '2012-09-07 11:30:00', 2),\n(2505, 5, 3, '2012-09-07 14:30:00', 2),\n(2506, 6, 0, '2012-09-07 09:30:00', 8),\n(2507, 6, 0, '2012-09-07 14:00:00', 2),\n(2508, 6, 6, '2012-09-07 16:30:00', 2),\n(2509, 7, 8, '2012-09-07 09:00:00', 2),\n(2510, 7, 9, '2012-09-07 11:30:00', 2),\n(2511, 7, 4, '2012-09-07 13:30:00', 2),\n(2512, 7, 15, '2012-09-07 15:00:00', 2),\n(2513, 7, 5, '2012-09-07 17:00:00', 2),\n(2514, 7, 5, '2012-09-07 19:00:00', 2),\n(2515, 8, 24, '2012-09-07 08:30:00', 1),\n(2516, 8, 3, '2012-09-07 10:30:00', 1),\n(2517, 8, 21, '2012-09-07 11:00:00', 1),\n(2518, 8, 3, '2012-09-07 11:30:00', 1),\n(2519, 8, 17, '2012-09-07 12:00:00', 1),\n(2520, 8, 3, '2012-09-07 13:00:00', 1),\n(2521, 8, 20, '2012-09-07 13:30:00', 1),\n(2522, 8, 16, '2012-09-07 14:00:00', 1),\n(2523, 8, 21, '2012-09-07 14:30:00', 2),\n(2524, 8, 4, '2012-09-07 15:30:00', 1),\n(2525, 8, 21, '2012-09-07 16:30:00', 1),\n(2526, 8, 2, '2012-09-07 18:00:00', 1),\n(2527, 8, 7, '2012-09-07 18:30:00', 1),\n(2528, 8, 16, '2012-09-07 20:00:00', 1),\n(2529, 0, 5, '2012-09-08 08:00:00', 3),\n(2530, 0, 0, '2012-09-08 09:30:00', 3),\n(2531, 0, 7, '2012-09-08 11:00:00', 3),\n(2532, 0, 0, '2012-09-08 12:30:00', 3),\n(2533, 0, 11, '2012-09-08 15:00:00', 3),\n(2534, 0, 17, '2012-09-08 16:30:00', 3),\n(2535, 0, 16, '2012-09-08 18:30:00', 3),\n(2536, 1, 10, '2012-09-08 08:00:00', 3),\n(2537, 1, 24, '2012-09-08 09:30:00', 3),\n(2538, 1, 9, '2012-09-08 11:30:00', 3),\n(2539, 1, 0, '2012-09-08 13:00:00', 3),\n(2540, 1, 9, '2012-09-08 15:00:00', 6),\n(2541, 2, 8, '2012-09-08 08:30:00', 3),\n(2542, 2, 21, '2012-09-08 10:00:00', 3),\n(2543, 2, 26, '2012-09-08 13:00:00', 3),\n(2544, 2, 1, '2012-09-08 15:00:00', 3),\n(2545, 2, 21, '2012-09-08 16:30:00', 3),\n(2546, 2, 1, '2012-09-08 18:30:00', 3),\n(2547, 3, 6, '2012-09-08 08:00:00', 2),\n(2548, 3, 2, '2012-09-08 09:00:00', 2),\n(2549, 3, 15, '2012-09-08 10:00:00', 2),\n(2550, 3, 11, '2012-09-08 12:00:00', 2),\n(2551, 3, 20, '2012-09-08 13:00:00', 2),\n(2552, 3, 20, '2012-09-08 16:00:00', 2),\n(2553, 3, 1, '2012-09-08 17:30:00', 2),\n(2554, 3, 9, '2012-09-08 18:30:00', 2),\n(2555, 3, 15, '2012-09-08 19:30:00', 2),\n(2556, 4, 20, '2012-09-08 08:00:00', 2),\n(2557, 4, 0, '2012-09-08 09:30:00', 8),\n(2558, 4, 3, '2012-09-08 13:30:00', 2),\n(2559, 4, 0, '2012-09-08 14:30:00', 4),\n(2560, 4, 13, '2012-09-08 16:30:00', 2),\n(2561, 4, 13, '2012-09-08 18:00:00', 2),\n(2562, 4, 0, '2012-09-08 19:00:00', 2),\n(2563, 5, 24, '2012-09-08 15:30:00', 2),\n(2564, 6, 0, '2012-09-08 09:00:00', 2),\n(2565, 6, 6, '2012-09-08 13:30:00', 2),\n(2566, 6, 0, '2012-09-08 16:00:00', 2),\n(2567, 6, 4, '2012-09-08 17:00:00', 2),\n(2568, 6, 0, '2012-09-08 18:00:00', 4),\n(2569, 7, 6, '2012-09-08 09:30:00', 2),\n(2570, 7, 4, '2012-09-08 11:30:00', 2),\n(2571, 7, 13, '2012-09-08 12:30:00', 2),\n(2572, 7, 0, '2012-09-08 13:30:00', 2),\n(2573, 7, 15, '2012-09-08 14:30:00', 2),\n(2574, 7, 8, '2012-09-08 15:30:00', 2),\n(2575, 7, 1, '2012-09-08 16:30:00', 2),\n(2576, 7, 15, '2012-09-08 18:00:00', 2),\n(2577, 8, 21, '2012-09-08 08:00:00', 1),\n(2578, 8, 3, '2012-09-08 08:30:00', 1),\n(2579, 8, 22, '2012-09-08 09:00:00', 1),\n(2580, 8, 0, '2012-09-08 09:30:00', 1),\n(2581, 8, 16, '2012-09-08 10:00:00', 1),\n(2582, 8, 3, '2012-09-08 10:30:00', 2),\n(2583, 8, 0, '2012-09-08 11:30:00', 1),\n(2584, 8, 6, '2012-09-08 13:00:00', 1),\n(2585, 8, 22, '2012-09-08 15:30:00', 1),\n(2586, 8, 16, '2012-09-08 16:30:00', 1),\n(2587, 8, 7, '2012-09-08 17:00:00', 1),\n(2588, 8, 3, '2012-09-08 17:30:00', 1),\n(2589, 8, 8, '2012-09-08 19:30:00', 1),\n(2590, 0, 5, '2012-09-09 08:00:00', 3),\n(2591, 0, 16, '2012-09-09 09:30:00', 3),\n(2592, 0, 26, '2012-09-09 12:00:00', 3),\n(2593, 0, 7, '2012-09-09 15:00:00', 3),\n(2594, 0, 0, '2012-09-09 17:00:00', 3),\n(2595, 0, 24, '2012-09-09 18:30:00', 3),\n(2596, 1, 8, '2012-09-09 08:00:00', 3),\n(2597, 1, 0, '2012-09-09 10:00:00', 3),\n(2598, 1, 16, '2012-09-09 13:00:00', 3),\n(2599, 1, 10, '2012-09-09 14:30:00', 3),\n(2600, 1, 15, '2012-09-09 16:00:00', 3),\n(2601, 1, 0, '2012-09-09 17:30:00', 6),\n(2602, 2, 21, '2012-09-09 08:30:00', 3),\n(2603, 2, 1, '2012-09-09 11:00:00', 3),\n(2604, 2, 1, '2012-09-09 13:00:00', 6),\n(2605, 2, 5, '2012-09-09 16:30:00', 3),\n(2606, 2, 14, '2012-09-09 18:30:00', 3),\n(2607, 3, 22, '2012-09-09 09:00:00', 2),\n(2608, 3, 10, '2012-09-09 10:00:00', 2),\n(2609, 3, 20, '2012-09-09 13:00:00', 2),\n(2610, 3, 0, '2012-09-09 15:30:00', 2),\n(2611, 3, 21, '2012-09-09 16:30:00', 2),\n(2612, 3, 0, '2012-09-09 18:00:00', 2),\n(2613, 3, 6, '2012-09-09 19:00:00', 2),\n(2614, 4, 13, '2012-09-09 08:00:00', 2),\n(2615, 4, 7, '2012-09-09 09:00:00', 2),\n(2616, 4, 20, '2012-09-09 10:00:00', 2),\n(2617, 4, 0, '2012-09-09 11:00:00', 4),\n(2618, 4, 3, '2012-09-09 13:30:00', 2),\n(2619, 4, 11, '2012-09-09 14:30:00', 2),\n(2620, 4, 20, '2012-09-09 15:30:00', 2),\n(2621, 4, 0, '2012-09-09 17:00:00', 2),\n(2622, 4, 11, '2012-09-09 18:00:00', 2),\n(2623, 4, 13, '2012-09-09 19:00:00', 2),\n(2624, 5, 0, '2012-09-09 14:00:00', 2),\n(2625, 6, 0, '2012-09-09 08:30:00', 2),\n(2626, 6, 0, '2012-09-09 11:00:00', 6),\n(2627, 6, 12, '2012-09-09 14:00:00', 2),\n(2628, 6, 14, '2012-09-09 15:30:00', 2),\n(2629, 6, 0, '2012-09-09 16:30:00', 4),\n(2630, 6, 26, '2012-09-09 18:30:00', 2),\n(2631, 6, 21, '2012-09-09 19:30:00', 2),\n(2632, 7, 22, '2012-09-09 08:00:00', 2),\n(2633, 7, 22, '2012-09-09 10:30:00', 2),\n(2634, 7, 21, '2012-09-09 14:00:00', 2),\n(2635, 7, 4, '2012-09-09 17:00:00', 2),\n(2636, 7, 7, '2012-09-09 18:00:00', 2),\n(2637, 7, 4, '2012-09-09 19:30:00', 2),\n(2638, 8, 16, '2012-09-09 08:00:00', 1),\n(2639, 8, 0, '2012-09-09 08:30:00', 1),\n(2640, 8, 16, '2012-09-09 09:00:00', 1),\n(2641, 8, 3, '2012-09-09 09:30:00', 1),\n(2642, 8, 2, '2012-09-09 10:00:00', 1),\n(2643, 8, 21, '2012-09-09 10:30:00', 1),\n(2644, 8, 5, '2012-09-09 11:00:00', 1),\n(2645, 8, 15, '2012-09-09 11:30:00', 1),\n(2646, 8, 3, '2012-09-09 12:00:00', 2),\n(2647, 8, 0, '2012-09-09 13:00:00', 1),\n(2648, 8, 0, '2012-09-09 14:30:00', 1),\n(2649, 8, 16, '2012-09-09 16:30:00', 1),\n(2650, 8, 9, '2012-09-09 17:00:00', 1),\n(2651, 8, 17, '2012-09-09 17:30:00', 1),\n(2652, 8, 6, '2012-09-09 18:00:00', 1),\n(2653, 8, 3, '2012-09-09 18:30:00', 1),\n(2654, 8, 16, '2012-09-09 19:00:00', 1),\n(2655, 8, 3, '2012-09-09 19:30:00', 1),\n(2656, 8, 16, '2012-09-09 20:00:00', 1),\n(2657, 0, 22, '2012-09-10 10:30:00', 3),\n(2658, 0, 14, '2012-09-10 12:00:00', 3),\n(2659, 0, 0, '2012-09-10 13:30:00', 3),\n(2660, 0, 14, '2012-09-10 15:30:00', 3),\n(2661, 0, 10, '2012-09-10 18:30:00', 3),\n(2662, 1, 24, '2012-09-10 08:00:00', 3),\n(2663, 1, 0, '2012-09-10 09:30:00', 3),\n(2664, 1, 0, '2012-09-10 13:00:00', 3),\n(2665, 1, 15, '2012-09-10 14:30:00', 3),\n(2666, 1, 0, '2012-09-10 16:00:00', 3),\n(2667, 1, 12, '2012-09-10 17:30:00', 3),\n(2668, 1, 0, '2012-09-10 19:00:00', 3),\n(2669, 2, 1, '2012-09-10 09:00:00', 6),\n(2670, 2, 21, '2012-09-10 12:00:00', 3),\n(2671, 2, 21, '2012-09-10 14:00:00', 3),\n(2672, 2, 0, '2012-09-10 15:30:00', 3),\n(2673, 2, 6, '2012-09-10 19:00:00', 3),\n(2674, 3, 6, '2012-09-10 08:30:00', 2),\n(2675, 3, 15, '2012-09-10 09:30:00', 2),\n(2676, 3, 15, '2012-09-10 11:00:00', 2),\n(2677, 3, 15, '2012-09-10 13:00:00', 2),\n(2678, 3, 16, '2012-09-10 15:00:00', 2),\n(2679, 3, 2, '2012-09-10 16:30:00', 2),\n(2680, 3, 16, '2012-09-10 17:30:00', 2),\n(2681, 3, 17, '2012-09-10 18:30:00', 2),\n(2682, 3, 15, '2012-09-10 19:30:00', 2),\n(2683, 4, 4, '2012-09-10 08:00:00', 2),\n(2684, 4, 13, '2012-09-10 09:00:00', 4),\n(2685, 4, 20, '2012-09-10 11:30:00', 2),\n(2686, 4, 11, '2012-09-10 12:30:00', 2),\n(2687, 4, 1, '2012-09-10 13:30:00', 2),\n(2688, 4, 10, '2012-09-10 14:30:00', 2),\n(2689, 4, 12, '2012-09-10 15:30:00', 2),\n(2690, 4, 17, '2012-09-10 17:00:00', 2),\n(2691, 4, 14, '2012-09-10 18:00:00', 2),\n(2692, 4, 0, '2012-09-10 19:00:00', 2),\n(2693, 5, 0, '2012-09-10 10:00:00', 2),\n(2694, 5, 0, '2012-09-10 11:30:00', 2),\n(2695, 6, 0, '2012-09-10 08:30:00', 2),\n(2696, 6, 11, '2012-09-10 09:30:00', 2),\n(2697, 6, 8, '2012-09-10 11:00:00', 2),\n(2698, 6, 12, '2012-09-10 12:30:00', 2),\n(2699, 6, 0, '2012-09-10 14:00:00', 6),\n(2700, 6, 0, '2012-09-10 17:30:00', 2),\n(2701, 6, 12, '2012-09-10 19:00:00', 2),\n(2702, 7, 22, '2012-09-10 09:30:00', 2),\n(2703, 7, 4, '2012-09-10 11:30:00', 2),\n(2704, 7, 24, '2012-09-10 15:00:00', 2),\n(2705, 7, 10, '2012-09-10 16:00:00', 2),\n(2706, 7, 15, '2012-09-10 17:30:00', 2),\n(2707, 7, 4, '2012-09-10 18:30:00', 2),\n(2708, 7, 7, '2012-09-10 19:30:00', 2),\n(2709, 8, 15, '2012-09-10 08:30:00', 1),\n(2710, 8, 26, '2012-09-10 10:30:00', 1),\n(2711, 8, 5, '2012-09-10 12:00:00', 1),\n(2712, 8, 16, '2012-09-10 12:30:00', 1),\n(2713, 8, 2, '2012-09-10 13:00:00', 1),\n(2714, 8, 16, '2012-09-10 13:30:00', 1),\n(2715, 8, 3, '2012-09-10 15:00:00', 1),\n(2716, 8, 21, '2012-09-10 15:30:00', 1),\n(2717, 8, 24, '2012-09-10 16:00:00', 1),\n(2718, 8, 16, '2012-09-10 16:30:00', 1),\n(2719, 8, 21, '2012-09-10 17:30:00', 1),\n(2720, 8, 3, '2012-09-10 19:30:00', 1),\n(2721, 8, 21, '2012-09-10 20:00:00', 1),\n(2722, 0, 5, '2012-09-11 09:00:00', 3),\n(2723, 0, 6, '2012-09-11 10:30:00', 3),\n(2724, 0, 7, '2012-09-11 12:00:00', 3),\n(2725, 0, 17, '2012-09-11 14:30:00', 3),\n(2726, 0, 11, '2012-09-11 16:00:00', 3),\n(2727, 0, 26, '2012-09-11 19:00:00', 3),\n(2728, 1, 9, '2012-09-11 08:00:00', 3),\n(2729, 1, 11, '2012-09-11 09:30:00', 3),\n(2730, 1, 8, '2012-09-11 11:00:00', 3),\n(2731, 1, 12, '2012-09-11 12:30:00', 3),\n(2732, 1, 11, '2012-09-11 14:30:00', 3),\n(2733, 1, 9, '2012-09-11 16:00:00', 3),\n(2734, 1, 11, '2012-09-11 17:30:00', 6),\n(2735, 2, 2, '2012-09-11 11:00:00', 3),\n(2736, 2, 1, '2012-09-11 12:30:00', 3),\n(2737, 2, 8, '2012-09-11 14:00:00', 3),\n(2738, 2, 21, '2012-09-11 17:00:00', 6),\n(2739, 3, 22, '2012-09-11 08:00:00', 2),\n(2740, 3, 16, '2012-09-11 09:30:00', 2),\n(2741, 3, 21, '2012-09-11 11:00:00', 2),\n(2742, 3, 6, '2012-09-11 12:00:00', 2),\n(2743, 3, 15, '2012-09-11 13:30:00', 2),\n(2744, 3, 2, '2012-09-11 18:00:00', 2),\n(2745, 3, 6, '2012-09-11 19:30:00', 2),\n(2746, 4, 3, '2012-09-11 08:00:00', 2),\n(2747, 4, 6, '2012-09-11 09:00:00', 2),\n(2748, 4, 0, '2012-09-11 10:00:00', 4),\n(2749, 4, 13, '2012-09-11 12:30:00', 2),\n(2750, 4, 16, '2012-09-11 13:30:00', 2),\n(2751, 4, 3, '2012-09-11 14:30:00', 2),\n(2752, 4, 0, '2012-09-11 15:30:00', 2),\n(2753, 4, 8, '2012-09-11 16:30:00', 2),\n(2754, 4, 0, '2012-09-11 18:00:00', 2),\n(2755, 4, 14, '2012-09-11 19:00:00', 2),\n(2756, 5, 0, '2012-09-11 11:30:00', 2),\n(2757, 5, 0, '2012-09-11 18:00:00', 2),\n(2758, 6, 12, '2012-09-11 08:00:00', 2),\n(2759, 6, 0, '2012-09-11 09:00:00', 2),\n(2760, 6, 12, '2012-09-11 10:30:00', 4),\n(2761, 6, 0, '2012-09-11 12:30:00', 4),\n(2762, 6, 16, '2012-09-11 14:30:00', 2),\n(2763, 6, 0, '2012-09-11 15:30:00', 4),\n(2764, 6, 12, '2012-09-11 17:30:00', 2),\n(2765, 6, 0, '2012-09-11 18:30:00', 2),\n(2766, 6, 12, '2012-09-11 19:30:00', 2),\n(2767, 7, 10, '2012-09-11 08:30:00', 2),\n(2768, 7, 13, '2012-09-11 09:30:00', 2),\n(2769, 7, 7, '2012-09-11 11:00:00', 2),\n(2770, 7, 6, '2012-09-11 13:30:00', 2),\n(2771, 7, 4, '2012-09-11 14:30:00', 2),\n(2772, 7, 24, '2012-09-11 16:30:00', 2),\n(2773, 7, 10, '2012-09-11 18:00:00', 2),\n(2774, 7, 15, '2012-09-11 19:00:00', 2),\n(2775, 8, 24, '2012-09-11 08:30:00', 1),\n(2776, 8, 3, '2012-09-11 09:00:00', 1),\n(2777, 8, 21, '2012-09-11 09:30:00', 1),\n(2778, 8, 0, '2012-09-11 10:30:00', 1),\n(2779, 8, 16, '2012-09-11 11:00:00', 1),\n(2780, 8, 3, '2012-09-11 12:00:00', 2),\n(2781, 8, 21, '2012-09-11 13:00:00', 1),\n(2782, 8, 21, '2012-09-11 14:00:00', 2),\n(2783, 8, 22, '2012-09-11 15:00:00', 1),\n(2784, 8, 8, '2012-09-11 15:30:00', 1),\n(2785, 8, 3, '2012-09-11 17:00:00', 2),\n(2786, 8, 3, '2012-09-11 18:30:00', 2),\n(2787, 0, 22, '2012-09-12 08:30:00', 3),\n(2788, 0, 0, '2012-09-12 10:00:00', 3),\n(2789, 0, 4, '2012-09-12 11:30:00', 3),\n(2790, 0, 26, '2012-09-12 13:00:00', 3),\n(2791, 0, 5, '2012-09-12 15:00:00', 3),\n(2792, 0, 0, '2012-09-12 16:30:00', 3),\n(2793, 0, 16, '2012-09-12 18:00:00', 3),\n(2794, 1, 11, '2012-09-12 08:30:00', 3),\n(2795, 1, 0, '2012-09-12 10:00:00', 3),\n(2796, 1, 14, '2012-09-12 12:00:00', 3),\n(2797, 1, 11, '2012-09-12 13:30:00', 3),\n(2798, 1, 0, '2012-09-12 15:00:00', 6),\n(2799, 1, 10, '2012-09-12 18:30:00', 3),\n(2800, 2, 24, '2012-09-12 08:00:00', 3),\n(2801, 2, 12, '2012-09-12 09:30:00', 3),\n(2802, 2, 9, '2012-09-12 11:00:00', 3),\n(2803, 2, 13, '2012-09-12 14:30:00', 3),\n(2804, 2, 9, '2012-09-12 16:00:00', 3),\n(2805, 2, 2, '2012-09-12 17:30:00', 6),\n(2806, 3, 15, '2012-09-12 09:00:00', 2),\n(2807, 3, 20, '2012-09-12 12:30:00', 2),\n(2808, 3, 10, '2012-09-12 13:30:00', 2),\n(2809, 3, 3, '2012-09-12 14:30:00', 2),\n(2810, 3, 16, '2012-09-12 15:30:00', 2),\n(2811, 3, 0, '2012-09-12 19:00:00', 2),\n(2812, 4, 16, '2012-09-12 08:00:00', 2),\n(2813, 4, 0, '2012-09-12 09:00:00', 2),\n(2814, 4, 0, '2012-09-12 10:30:00', 2),\n(2815, 4, 13, '2012-09-12 11:30:00', 2),\n(2816, 4, 0, '2012-09-12 12:30:00', 4),\n(2817, 4, 16, '2012-09-12 14:30:00', 2),\n(2818, 4, 0, '2012-09-12 15:30:00', 2),\n(2819, 4, 3, '2012-09-12 16:30:00', 2),\n(2820, 4, 1, '2012-09-12 17:30:00', 2),\n(2821, 4, 7, '2012-09-12 19:00:00', 2),\n(2822, 5, 0, '2012-09-12 16:30:00', 2),\n(2823, 6, 0, '2012-09-12 08:30:00', 4),\n(2824, 6, 0, '2012-09-12 11:00:00', 6),\n(2825, 6, 24, '2012-09-12 14:00:00', 2),\n(2826, 6, 0, '2012-09-12 15:00:00', 4),\n(2827, 6, 0, '2012-09-12 17:30:00', 4),\n(2828, 7, 5, '2012-09-12 08:30:00', 2),\n(2829, 7, 4, '2012-09-12 09:30:00', 2),\n(2830, 7, 15, '2012-09-12 10:30:00', 2),\n(2831, 7, 24, '2012-09-12 13:00:00', 2),\n(2832, 7, 7, '2012-09-12 15:30:00', 2),\n(2833, 7, 22, '2012-09-12 17:30:00', 2),\n(2834, 8, 1, '2012-09-12 10:00:00', 1),\n(2835, 8, 16, '2012-09-12 11:00:00', 1),\n(2836, 8, 3, '2012-09-12 11:30:00', 1),\n(2837, 8, 16, '2012-09-12 12:00:00', 1),\n(2838, 8, 21, '2012-09-12 12:30:00', 2),\n(2839, 8, 1, '2012-09-12 13:30:00', 1),\n(2840, 8, 5, '2012-09-12 14:00:00', 1),\n(2841, 8, 2, '2012-09-12 14:30:00', 1),\n(2842, 8, 22, '2012-09-12 15:30:00', 1),\n(2843, 8, 3, '2012-09-12 16:00:00', 1),\n(2844, 8, 4, '2012-09-12 18:00:00', 1),\n(2845, 8, 21, '2012-09-12 18:30:00', 2),\n(2846, 0, 5, '2012-09-13 09:00:00', 3),\n(2847, 0, 0, '2012-09-13 10:30:00', 6),\n(2848, 0, 7, '2012-09-13 13:30:00', 3),\n(2849, 0, 10, '2012-09-13 16:00:00', 3),\n(2850, 0, 0, '2012-09-13 17:30:00', 6),\n(2851, 1, 8, '2012-09-13 08:30:00', 3),\n(2852, 1, 11, '2012-09-13 10:30:00', 3),\n(2853, 1, 0, '2012-09-13 12:00:00', 6),\n(2854, 1, 12, '2012-09-13 15:00:00', 3),\n(2855, 1, 8, '2012-09-13 16:30:00', 3),\n(2856, 1, 24, '2012-09-13 18:30:00', 3),\n(2857, 2, 11, '2012-09-13 08:00:00', 3),\n(2858, 2, 1, '2012-09-13 09:30:00', 3),\n(2859, 2, 2, '2012-09-13 11:00:00', 3),\n(2860, 2, 10, '2012-09-13 13:00:00', 3),\n(2861, 2, 15, '2012-09-13 14:30:00', 3),\n(2862, 2, 21, '2012-09-13 16:30:00', 3),\n(2863, 2, 11, '2012-09-13 18:00:00', 3),\n(2864, 3, 16, '2012-09-13 08:00:00', 2),\n(2865, 3, 3, '2012-09-13 09:00:00', 2),\n(2866, 3, 17, '2012-09-13 10:00:00', 2),\n(2867, 3, 22, '2012-09-13 11:30:00', 2),\n(2868, 3, 24, '2012-09-13 13:00:00', 2),\n(2869, 3, 3, '2012-09-13 14:00:00', 2),\n(2870, 3, 11, '2012-09-13 16:00:00', 2),\n(2871, 3, 3, '2012-09-13 17:30:00', 2),\n(2872, 3, 17, '2012-09-13 18:30:00', 2),\n(2873, 3, 4, '2012-09-13 19:30:00', 2),\n(2874, 4, 7, '2012-09-13 08:00:00', 2),\n(2875, 4, 20, '2012-09-13 09:00:00', 2),\n(2876, 4, 6, '2012-09-13 10:30:00', 2),\n(2877, 4, 5, '2012-09-13 11:30:00', 2),\n(2878, 4, 21, '2012-09-13 12:30:00', 2),\n(2879, 4, 20, '2012-09-13 14:00:00', 2),\n(2880, 4, 9, '2012-09-13 15:30:00', 2),\n(2881, 4, 20, '2012-09-13 17:00:00', 2),\n(2882, 4, 0, '2012-09-13 18:00:00', 2),\n(2883, 4, 5, '2012-09-13 19:00:00', 2),\n(2884, 5, 0, '2012-09-13 08:30:00', 2),\n(2885, 5, 0, '2012-09-13 16:00:00', 2),\n(2886, 5, 0, '2012-09-13 19:00:00', 2),\n(2887, 6, 12, '2012-09-13 09:00:00', 2),\n(2888, 6, 0, '2012-09-13 10:30:00', 14),\n(2889, 6, 0, '2012-09-13 18:00:00', 2),\n(2890, 6, 6, '2012-09-13 19:30:00', 2),\n(2891, 7, 14, '2012-09-13 08:00:00', 2),\n(2892, 7, 4, '2012-09-13 09:30:00', 2),\n(2893, 7, 17, '2012-09-13 12:30:00', 2),\n(2894, 7, 5, '2012-09-13 13:30:00', 2),\n(2895, 7, 4, '2012-09-13 14:30:00', 2),\n(2896, 7, 15, '2012-09-13 17:00:00', 2),\n(2897, 7, 0, '2012-09-13 18:00:00', 2),\n(2898, 7, 9, '2012-09-13 19:00:00', 2),\n(2899, 8, 20, '2012-09-13 08:00:00', 1),\n(2900, 8, 15, '2012-09-13 09:00:00', 1),\n(2901, 8, 21, '2012-09-13 09:30:00', 1),\n(2902, 8, 21, '2012-09-13 10:30:00', 1),\n(2903, 8, 16, '2012-09-13 11:00:00', 1),\n(2904, 8, 21, '2012-09-13 11:30:00', 1),\n(2905, 8, 0, '2012-09-13 12:00:00', 1),\n(2906, 8, 24, '2012-09-13 12:30:00', 1),\n(2907, 8, 3, '2012-09-13 13:30:00', 1),\n(2908, 8, 16, '2012-09-13 14:30:00', 1),\n(2909, 8, 21, '2012-09-13 15:00:00', 1),\n(2910, 8, 21, '2012-09-13 16:00:00', 1),\n(2911, 8, 16, '2012-09-13 18:00:00', 1),\n(2912, 8, 21, '2012-09-13 18:30:00', 1),\n(2913, 8, 0, '2012-09-13 19:00:00', 1),\n(2914, 8, 21, '2012-09-13 19:30:00', 1),\n(2915, 8, 15, '2012-09-13 20:00:00', 1),\n(2916, 0, 6, '2012-09-14 08:00:00', 3),\n(2917, 0, 17, '2012-09-14 10:00:00', 3),\n(2918, 0, 5, '2012-09-14 12:30:00', 3),\n(2919, 0, 3, '2012-09-14 14:00:00', 3),\n(2920, 0, 0, '2012-09-14 16:00:00', 3),\n(2921, 0, 26, '2012-09-14 17:30:00', 3),\n(2922, 0, 0, '2012-09-14 19:00:00', 3),\n(2923, 1, 11, '2012-09-14 08:00:00', 6),\n(2924, 1, 8, '2012-09-14 11:00:00', 6),\n(2925, 1, 0, '2012-09-14 14:00:00', 3),\n(2926, 1, 0, '2012-09-14 17:00:00', 6),\n(2927, 2, 1, '2012-09-14 08:00:00', 3),\n(2928, 2, 21, '2012-09-14 11:00:00', 3),\n(2929, 2, 1, '2012-09-14 13:00:00', 3),\n(2930, 2, 5, '2012-09-14 16:00:00', 3),\n(2931, 2, 9, '2012-09-14 18:00:00', 3),\n(2932, 3, 15, '2012-09-14 08:30:00', 2),\n(2933, 3, 16, '2012-09-14 11:00:00', 2),\n(2934, 3, 20, '2012-09-14 12:30:00', 2),\n(2935, 3, 21, '2012-09-14 18:30:00', 2),\n(2936, 4, 14, '2012-09-14 08:00:00', 2),\n(2937, 4, 0, '2012-09-14 09:00:00', 2),\n(2938, 4, 13, '2012-09-14 11:00:00', 2),\n(2939, 4, 9, '2012-09-14 12:00:00', 2),\n(2940, 4, 0, '2012-09-14 13:00:00', 2),\n(2941, 4, 13, '2012-09-14 14:00:00', 4),\n(2942, 4, 0, '2012-09-14 16:00:00', 2),\n(2943, 4, 6, '2012-09-14 18:00:00', 2),\n(2944, 4, 20, '2012-09-14 19:00:00', 2),\n(2945, 5, 15, '2012-09-14 09:30:00', 2),\n(2946, 5, 0, '2012-09-14 11:00:00', 4),\n(2947, 6, 12, '2012-09-14 08:30:00', 2),\n(2948, 6, 0, '2012-09-14 09:30:00', 4),\n(2949, 6, 0, '2012-09-14 12:30:00', 2),\n(2950, 6, 16, '2012-09-14 14:00:00', 2),\n(2951, 6, 0, '2012-09-14 15:00:00', 2),\n(2952, 6, 12, '2012-09-14 16:00:00', 2),\n(2953, 6, 17, '2012-09-14 17:30:00', 2),\n(2954, 7, 10, '2012-09-14 08:30:00', 2),\n(2955, 7, 24, '2012-09-14 12:00:00', 2),\n(2956, 7, 9, '2012-09-14 13:30:00', 2),\n(2957, 7, 21, '2012-09-14 16:30:00', 2),\n(2958, 7, 24, '2012-09-14 18:00:00', 2),\n(2959, 8, 3, '2012-09-14 08:00:00', 1),\n(2960, 8, 16, '2012-09-14 08:30:00', 1),\n(2961, 8, 2, '2012-09-14 09:00:00', 1),\n(2962, 8, 21, '2012-09-14 09:30:00', 1),\n(2963, 8, 3, '2012-09-14 10:00:00', 1),\n(2964, 8, 9, '2012-09-14 10:30:00', 1),\n(2965, 8, 3, '2012-09-14 11:00:00', 2),\n(2966, 8, 20, '2012-09-14 12:00:00', 1),\n(2967, 8, 21, '2012-09-14 13:00:00', 1),\n(2968, 8, 16, '2012-09-14 13:30:00', 1),\n(2969, 8, 24, '2012-09-14 14:00:00', 1),\n(2970, 8, 20, '2012-09-14 15:00:00', 1),\n(2971, 8, 22, '2012-09-14 15:30:00', 1),\n(2972, 8, 16, '2012-09-14 16:00:00', 1),\n(2973, 8, 3, '2012-09-14 16:30:00', 1),\n(2974, 8, 15, '2012-09-14 17:00:00', 1),\n(2975, 8, 16, '2012-09-14 17:30:00', 2),\n(2976, 8, 11, '2012-09-14 19:00:00', 1),\n(2977, 8, 2, '2012-09-14 19:30:00', 1),\n(2978, 0, 0, '2012-09-15 08:00:00', 12),\n(2979, 0, 11, '2012-09-15 14:00:00', 3),\n(2980, 0, 7, '2012-09-15 16:30:00', 3),\n(2981, 0, 17, '2012-09-15 18:00:00', 3),\n(2982, 1, 10, '2012-09-15 08:00:00', 3),\n(2983, 1, 11, '2012-09-15 10:00:00', 3),\n(2984, 1, 24, '2012-09-15 13:00:00', 6),\n(2985, 1, 12, '2012-09-15 16:00:00', 3),\n(2986, 2, 0, '2012-09-15 08:00:00', 3),\n(2987, 2, 1, '2012-09-15 10:30:00', 3),\n(2988, 2, 0, '2012-09-15 12:00:00', 3),\n(2989, 2, 14, '2012-09-15 13:30:00', 3),\n(2990, 2, 26, '2012-09-15 15:30:00', 3),\n(2991, 2, 0, '2012-09-15 17:30:00', 3),\n(2992, 3, 1, '2012-09-15 08:00:00', 2),\n(2993, 3, 14, '2012-09-15 09:30:00', 2),\n(2994, 3, 22, '2012-09-15 10:30:00', 2),\n(2995, 3, 21, '2012-09-15 11:30:00', 2),\n(2996, 3, 20, '2012-09-15 12:30:00', 2),\n(2997, 3, 3, '2012-09-15 14:30:00', 2),\n(2998, 3, 11, '2012-09-15 15:30:00', 2),\n(2999, 3, 0, '2012-09-15 17:30:00', 2),\n(3000, 3, 11, '2012-09-15 19:30:00', 2),\n(3001, 4, 13, '2012-09-15 08:00:00', 2),\n(3002, 4, 0, '2012-09-15 09:00:00', 2),\n(3003, 4, 17, '2012-09-15 10:00:00', 2),\n(3004, 4, 3, '2012-09-15 11:00:00', 2),\n(3005, 4, 0, '2012-09-15 12:00:00', 8),\n(3006, 4, 24, '2012-09-15 16:00:00', 2),\n(3007, 4, 16, '2012-09-15 17:00:00', 4),\n(3008, 4, 14, '2012-09-15 19:00:00', 2),\n(3009, 5, 0, '2012-09-15 12:30:00', 2),\n(3010, 6, 0, '2012-09-15 08:00:00', 2),\n(3011, 6, 0, '2012-09-15 09:30:00', 4),\n(3012, 6, 11, '2012-09-15 11:30:00', 2),\n(3013, 6, 22, '2012-09-15 12:30:00', 2),\n(3014, 6, 12, '2012-09-15 14:00:00', 2),\n(3015, 6, 1, '2012-09-15 15:00:00', 2),\n(3016, 6, 4, '2012-09-15 16:00:00', 2),\n(3017, 6, 15, '2012-09-15 17:30:00', 2),\n(3018, 6, 0, '2012-09-15 18:30:00', 4),\n(3019, 7, 17, '2012-09-15 08:30:00', 2),\n(3020, 7, 2, '2012-09-15 09:30:00', 2),\n(3021, 7, 8, '2012-09-15 10:30:00', 2),\n(3022, 7, 15, '2012-09-15 13:00:00', 2),\n(3023, 7, 22, '2012-09-15 14:00:00', 2),\n(3024, 7, 13, '2012-09-15 15:00:00', 2),\n(3025, 7, 10, '2012-09-15 16:00:00', 2),\n(3026, 7, 13, '2012-09-15 19:30:00', 2),\n(3027, 8, 21, '2012-09-15 08:00:00', 1),\n(3028, 8, 16, '2012-09-15 08:30:00', 1),\n(3029, 8, 15, '2012-09-15 09:00:00', 1),\n(3030, 8, 16, '2012-09-15 09:30:00', 1),\n(3031, 8, 15, '2012-09-15 10:30:00', 1),\n(3032, 8, 16, '2012-09-15 11:00:00', 2),\n(3033, 8, 3, '2012-09-15 12:00:00', 1),\n(3034, 8, 21, '2012-09-15 12:30:00', 2),\n(3035, 8, 6, '2012-09-15 13:30:00', 1),\n(3036, 8, 15, '2012-09-15 15:00:00', 1),\n(3037, 8, 6, '2012-09-15 15:30:00', 1),\n(3038, 8, 21, '2012-09-15 16:30:00', 1),\n(3039, 8, 21, '2012-09-15 19:00:00', 1),\n(3040, 8, 3, '2012-09-15 19:30:00', 1),\n(3041, 0, 0, '2012-09-16 08:00:00', 9),\n(3042, 0, 11, '2012-09-16 12:30:00', 3),\n(3043, 0, 6, '2012-09-16 14:00:00', 3),\n(3044, 0, 0, '2012-09-16 15:30:00', 3),\n(3045, 0, 24, '2012-09-16 17:00:00', 3),\n(3046, 0, 10, '2012-09-16 18:30:00', 3),\n(3047, 1, 8, '2012-09-16 08:00:00', 3),\n(3048, 1, 0, '2012-09-16 09:30:00', 6),\n(3049, 1, 16, '2012-09-16 12:30:00', 3),\n(3050, 1, 8, '2012-09-16 14:00:00', 3),\n(3051, 1, 12, '2012-09-16 15:30:00', 3),\n(3052, 1, 0, '2012-09-16 17:30:00', 6),\n(3053, 2, 2, '2012-09-16 08:30:00', 3),\n(3054, 2, 1, '2012-09-16 10:30:00', 3),\n(3055, 2, 12, '2012-09-16 12:00:00', 3),\n(3056, 2, 21, '2012-09-16 13:30:00', 3),\n(3057, 2, 7, '2012-09-16 15:30:00', 3),\n(3058, 2, 21, '2012-09-16 17:00:00', 3),\n(3059, 2, 21, '2012-09-16 19:00:00', 3),\n(3060, 3, 1, '2012-09-16 09:00:00', 2),\n(3061, 3, 14, '2012-09-16 10:00:00', 2),\n(3062, 3, 0, '2012-09-16 13:00:00', 2),\n(3063, 3, 22, '2012-09-16 16:30:00', 2),\n(3064, 3, 16, '2012-09-16 17:30:00', 2),\n(3065, 3, 15, '2012-09-16 18:30:00', 2),\n(3066, 4, 1, '2012-09-16 08:00:00', 2),\n(3067, 4, 0, '2012-09-16 09:00:00', 2),\n(3068, 4, 8, '2012-09-16 10:00:00', 4),\n(3069, 4, 13, '2012-09-16 12:00:00', 4),\n(3070, 4, 3, '2012-09-16 14:00:00', 4),\n(3071, 4, 0, '2012-09-16 16:00:00', 2),\n(3072, 4, 0, '2012-09-16 17:30:00', 4),\n(3073, 4, 14, '2012-09-16 19:30:00', 2),\n(3074, 5, 22, '2012-09-16 08:30:00', 2),\n(3075, 6, 11, '2012-09-16 08:00:00', 2),\n(3076, 6, 0, '2012-09-16 09:00:00', 2),\n(3077, 6, 12, '2012-09-16 10:30:00', 2),\n(3078, 6, 2, '2012-09-16 12:00:00', 2),\n(3079, 6, 10, '2012-09-16 13:30:00', 2),\n(3080, 6, 0, '2012-09-16 14:30:00', 4),\n(3081, 6, 0, '2012-09-16 17:30:00', 6),\n(3082, 7, 10, '2012-09-16 08:30:00', 2),\n(3083, 7, 10, '2012-09-16 10:30:00', 2),\n(3084, 7, 9, '2012-09-16 11:30:00', 2),\n(3085, 7, 15, '2012-09-16 12:30:00', 2),\n(3086, 7, 13, '2012-09-16 14:00:00', 2),\n(3087, 7, 8, '2012-09-16 15:30:00', 2),\n(3088, 7, 27, '2012-09-16 16:30:00', 2),\n(3089, 7, 27, '2012-09-16 19:00:00', 2),\n(3090, 8, 21, '2012-09-16 09:30:00', 1),\n(3091, 8, 3, '2012-09-16 10:30:00', 2),\n(3092, 8, 21, '2012-09-16 12:00:00', 1),\n(3093, 8, 27, '2012-09-16 13:30:00', 1),\n(3094, 8, 16, '2012-09-16 14:30:00', 1),\n(3095, 8, 21, '2012-09-16 15:00:00', 1),\n(3096, 8, 27, '2012-09-16 15:30:00', 1),\n(3097, 8, 16, '2012-09-16 16:30:00', 1),\n(3098, 8, 3, '2012-09-16 17:00:00', 1),\n(3099, 8, 3, '2012-09-16 18:00:00', 1),\n(3100, 8, 2, '2012-09-16 19:00:00', 1),\n(3101, 8, 3, '2012-09-16 20:00:00', 1),\n(3102, 0, 22, '2012-09-17 08:00:00', 3),\n(3103, 0, 0, '2012-09-17 09:30:00', 3),\n(3104, 0, 13, '2012-09-17 11:00:00', 3),\n(3105, 0, 7, '2012-09-17 14:00:00', 3),\n(3106, 0, 0, '2012-09-17 16:30:00', 3),\n(3107, 0, 26, '2012-09-17 18:00:00', 3),\n(3108, 1, 8, '2012-09-17 08:30:00', 3),\n(3109, 1, 9, '2012-09-17 10:00:00', 3),\n(3110, 1, 0, '2012-09-17 11:30:00', 3),\n(3111, 1, 0, '2012-09-17 13:30:00', 3),\n(3112, 1, 8, '2012-09-17 16:00:00', 3),\n(3113, 1, 9, '2012-09-17 17:30:00', 3),\n(3114, 1, 8, '2012-09-17 19:00:00', 3),\n(3115, 2, 5, '2012-09-17 08:30:00', 3),\n(3116, 2, 12, '2012-09-17 10:00:00', 3),\n(3117, 2, 21, '2012-09-17 12:00:00', 3),\n(3118, 2, 12, '2012-09-17 13:30:00', 3),\n(3119, 2, 0, '2012-09-17 15:00:00', 3),\n(3120, 2, 1, '2012-09-17 18:00:00', 3),\n(3121, 3, 21, '2012-09-17 08:30:00', 2),\n(3122, 3, 22, '2012-09-17 09:30:00', 4),\n(3123, 3, 15, '2012-09-17 12:00:00', 2),\n(3124, 3, 22, '2012-09-17 14:00:00', 2),\n(3125, 3, 16, '2012-09-17 16:30:00', 2),\n(3126, 3, 13, '2012-09-17 17:30:00', 2),\n(3127, 3, 3, '2012-09-17 18:30:00', 2),\n(3128, 4, 7, '2012-09-17 08:00:00', 2),\n(3129, 4, 0, '2012-09-17 09:30:00', 2),\n(3130, 4, 0, '2012-09-17 11:00:00', 4),\n(3131, 4, 10, '2012-09-17 13:00:00', 2),\n(3132, 4, 20, '2012-09-17 14:30:00', 2),\n(3133, 4, 16, '2012-09-17 15:30:00', 2),\n(3134, 4, 0, '2012-09-17 16:30:00', 2),\n(3135, 4, 5, '2012-09-17 17:30:00', 2),\n(3136, 4, 14, '2012-09-17 18:30:00', 2),\n(3137, 4, 0, '2012-09-17 19:30:00', 2),\n(3138, 5, 0, '2012-09-17 12:00:00', 2),\n(3139, 5, 24, '2012-09-17 15:00:00', 2),\n(3140, 6, 0, '2012-09-17 08:00:00', 4),\n(3141, 6, 0, '2012-09-17 10:30:00', 4),\n(3142, 6, 5, '2012-09-17 12:30:00', 2),\n(3143, 6, 0, '2012-09-17 13:30:00', 2),\n(3144, 6, 0, '2012-09-17 15:00:00', 8),\n(3145, 7, 10, '2012-09-17 08:00:00', 2),\n(3146, 7, 21, '2012-09-17 10:00:00', 2),\n(3147, 7, 17, '2012-09-17 11:00:00', 2),\n(3148, 7, 15, '2012-09-17 14:00:00', 2),\n(3149, 7, 0, '2012-09-17 15:00:00', 2),\n(3150, 7, 22, '2012-09-17 16:30:00', 2),\n(3151, 7, 20, '2012-09-17 18:30:00', 2),\n(3152, 7, 1, '2012-09-17 19:30:00', 2),\n(3153, 8, 15, '2012-09-17 09:30:00', 1),\n(3154, 8, 16, '2012-09-17 11:00:00', 1),\n(3155, 8, 0, '2012-09-17 12:00:00', 1),\n(3156, 8, 8, '2012-09-17 12:30:00', 1),\n(3157, 8, 1, '2012-09-17 13:30:00', 1),\n(3158, 8, 11, '2012-09-17 14:00:00', 1),\n(3159, 8, 3, '2012-09-17 15:00:00', 1),\n(3160, 8, 1, '2012-09-17 15:30:00', 1),\n(3161, 8, 2, '2012-09-17 16:00:00', 1),\n(3162, 8, 21, '2012-09-17 16:30:00', 1),\n(3163, 8, 3, '2012-09-17 17:00:00', 1),\n(3164, 8, 21, '2012-09-17 17:30:00', 1),\n(3165, 8, 21, '2012-09-17 19:00:00', 1),\n(3166, 8, 2, '2012-09-17 20:00:00', 1),\n(3167, 0, 28, '2012-09-18 09:00:00', 3),\n(3168, 0, 6, '2012-09-18 10:30:00', 3),\n(3169, 0, 11, '2012-09-18 12:00:00', 3),\n(3170, 0, 16, '2012-09-18 13:30:00', 3),\n(3171, 0, 5, '2012-09-18 16:00:00', 3),\n(3172, 0, 28, '2012-09-18 17:30:00', 3),\n(3173, 0, 14, '2012-09-18 19:00:00', 3),\n(3174, 1, 10, '2012-09-18 08:00:00', 3),\n(3175, 1, 12, '2012-09-18 09:30:00', 6),\n(3176, 1, 0, '2012-09-18 13:30:00', 3),\n(3177, 1, 11, '2012-09-18 16:00:00', 3),\n(3178, 1, 10, '2012-09-18 18:00:00', 3),\n(3179, 2, 16, '2012-09-18 08:00:00', 3),\n(3180, 2, 21, '2012-09-18 10:00:00', 3),\n(3181, 2, 21, '2012-09-18 13:00:00', 3),\n(3182, 2, 1, '2012-09-18 14:30:00', 3),\n(3183, 2, 24, '2012-09-18 16:00:00', 3),\n(3184, 2, 13, '2012-09-18 17:30:00', 3),\n(3185, 3, 3, '2012-09-18 08:00:00', 4),\n(3186, 3, 20, '2012-09-18 10:00:00', 2),\n(3187, 3, 13, '2012-09-18 11:30:00', 2),\n(3188, 3, 3, '2012-09-18 13:00:00', 4),\n(3189, 3, 21, '2012-09-18 17:00:00', 2),\n(3190, 3, 15, '2012-09-18 18:00:00', 2),\n(3191, 3, 3, '2012-09-18 19:30:00', 2),\n(3192, 4, 5, '2012-09-18 08:30:00', 2),\n(3193, 4, 0, '2012-09-18 09:30:00', 2),\n(3194, 4, 13, '2012-09-18 10:30:00', 2),\n(3195, 4, 1, '2012-09-18 11:30:00', 2),\n(3196, 4, 13, '2012-09-18 12:30:00', 2),\n(3197, 4, 0, '2012-09-18 15:00:00', 2),\n(3198, 4, 13, '2012-09-18 16:00:00', 2),\n(3199, 4, 0, '2012-09-18 17:00:00', 2),\n(3200, 4, 4, '2012-09-18 18:00:00', 2),\n(3201, 4, 0, '2012-09-18 19:00:00', 2),\n(3202, 5, 0, '2012-09-18 08:30:00', 2),\n(3203, 5, 0, '2012-09-18 18:00:00', 2),\n(3204, 6, 0, '2012-09-18 08:30:00', 2),\n(3205, 6, 9, '2012-09-18 09:30:00', 2),\n(3206, 6, 0, '2012-09-18 11:00:00', 2),\n(3207, 6, 8, '2012-09-18 14:00:00', 2),\n(3208, 6, 0, '2012-09-18 15:00:00', 6),\n(3209, 6, 0, '2012-09-18 18:30:00', 4),\n(3210, 7, 7, '2012-09-18 10:00:00', 2),\n(3211, 7, 0, '2012-09-18 11:00:00', 2),\n(3212, 7, 27, '2012-09-18 16:00:00', 2),\n(3213, 7, 8, '2012-09-18 18:00:00', 2),\n(3214, 8, 21, '2012-09-18 08:30:00', 1),\n(3215, 8, 15, '2012-09-18 10:00:00', 1),\n(3216, 8, 0, '2012-09-18 10:30:00', 1),\n(3217, 8, 7, '2012-09-18 11:00:00', 1),\n(3218, 8, 3, '2012-09-18 12:00:00', 1),\n(3219, 8, 28, '2012-09-18 13:30:00', 1),\n(3220, 8, 15, '2012-09-18 14:00:00', 1),\n(3221, 8, 4, '2012-09-18 14:30:00', 1),\n(3222, 8, 24, '2012-09-18 15:30:00', 1),\n(3223, 8, 3, '2012-09-18 16:00:00', 1),\n(3224, 8, 21, '2012-09-18 16:30:00', 1),\n(3225, 8, 9, '2012-09-18 17:00:00', 1),\n(3226, 8, 0, '2012-09-18 17:30:00', 1),\n(3227, 8, 3, '2012-09-18 18:00:00', 1),\n(3228, 8, 0, '2012-09-18 19:00:00', 1),\n(3229, 8, 28, '2012-09-18 20:00:00', 1),\n(3230, 0, 16, '2012-09-19 08:00:00', 3),\n(3231, 0, 28, '2012-09-19 09:30:00', 3),\n(3232, 0, 0, '2012-09-19 11:00:00', 6),\n(3233, 0, 28, '2012-09-19 15:00:00', 3),\n(3234, 0, 24, '2012-09-19 16:30:00', 3),\n(3235, 0, 14, '2012-09-19 18:00:00', 3),\n(3236, 1, 0, '2012-09-19 09:30:00', 3),\n(3237, 1, 1, '2012-09-19 11:00:00', 3),\n(3238, 1, 0, '2012-09-19 13:00:00', 3),\n(3239, 1, 4, '2012-09-19 16:00:00', 3),\n(3240, 1, 10, '2012-09-19 18:00:00', 3),\n(3241, 2, 1, '2012-09-19 08:00:00', 3),\n(3242, 2, 16, '2012-09-19 10:00:00', 3),\n(3243, 2, 9, '2012-09-19 11:30:00', 3),\n(3244, 2, 21, '2012-09-19 13:00:00', 3),\n(3245, 2, 29, '2012-09-19 14:30:00', 3),\n(3246, 2, 30, '2012-09-19 17:00:00', 3),\n(3247, 3, 22, '2012-09-19 08:00:00', 2),\n(3248, 3, 15, '2012-09-19 09:30:00', 2),\n(3249, 3, 3, '2012-09-19 10:30:00', 2),\n(3250, 3, 3, '2012-09-19 12:00:00', 2),\n(3251, 3, 2, '2012-09-19 13:00:00', 2),\n(3252, 3, 1, '2012-09-19 16:00:00', 2),\n(3253, 3, 21, '2012-09-19 19:00:00', 2),\n(3254, 4, 20, '2012-09-19 08:00:00', 2),\n(3255, 4, 5, '2012-09-19 09:00:00', 2),\n(3256, 4, 0, '2012-09-19 10:00:00', 4),\n(3257, 4, 5, '2012-09-19 12:00:00', 2),\n(3258, 4, 0, '2012-09-19 13:30:00', 6),\n(3259, 4, 16, '2012-09-19 17:00:00', 2),\n(3260, 4, 0, '2012-09-19 18:00:00', 2),\n(3261, 4, 13, '2012-09-19 19:00:00', 2),\n(3262, 5, 10, '2012-09-19 10:30:00', 2),\n(3263, 5, 7, '2012-09-19 12:30:00', 2),\n(3264, 5, 0, '2012-09-19 13:30:00', 2),\n(3265, 5, 0, '2012-09-19 15:30:00', 2),\n(3266, 5, 0, '2012-09-19 19:30:00', 2),\n(3267, 6, 0, '2012-09-19 08:00:00', 2),\n(3268, 6, 8, '2012-09-19 09:00:00', 2),\n(3269, 6, 14, '2012-09-19 10:00:00', 4),\n(3270, 6, 0, '2012-09-19 12:30:00', 2),\n(3271, 6, 12, '2012-09-19 13:30:00', 2),\n(3272, 6, 0, '2012-09-19 15:00:00', 2),\n(3273, 6, 12, '2012-09-19 16:30:00', 2),\n(3274, 6, 4, '2012-09-19 17:30:00', 2),\n(3275, 7, 10, '2012-09-19 08:00:00', 2),\n(3276, 7, 27, '2012-09-19 09:30:00', 2),\n(3277, 7, 15, '2012-09-19 10:30:00', 2),\n(3278, 7, 4, '2012-09-19 13:00:00', 4),\n(3279, 7, 27, '2012-09-19 15:00:00', 2),\n(3280, 7, 15, '2012-09-19 16:30:00', 2),\n(3281, 7, 6, '2012-09-19 18:00:00', 2),\n(3282, 8, 21, '2012-09-19 08:00:00', 1),\n(3283, 8, 3, '2012-09-19 08:30:00', 2),\n(3284, 8, 29, '2012-09-19 09:30:00', 1),\n(3285, 8, 3, '2012-09-19 10:00:00', 1),\n(3286, 8, 12, '2012-09-19 10:30:00', 1),\n(3287, 8, 30, '2012-09-19 11:30:00', 1),\n(3288, 8, 28, '2012-09-19 12:00:00', 1),\n(3289, 8, 29, '2012-09-19 12:30:00', 1),\n(3290, 8, 24, '2012-09-19 13:30:00', 1),\n(3291, 8, 29, '2012-09-19 14:00:00', 1),\n(3292, 8, 16, '2012-09-19 14:30:00', 2),\n(3293, 8, 22, '2012-09-19 17:30:00', 1),\n(3294, 8, 29, '2012-09-19 18:00:00', 1),\n(3295, 8, 3, '2012-09-19 18:30:00', 1),\n(3296, 8, 8, '2012-09-19 19:00:00', 1),\n(3297, 8, 15, '2012-09-19 19:30:00', 1),\n(3298, 0, 14, '2012-09-20 08:00:00', 3),\n(3299, 0, 0, '2012-09-20 09:30:00', 6),\n(3300, 0, 0, '2012-09-20 13:00:00', 3),\n(3301, 0, 17, '2012-09-20 15:30:00', 3),\n(3302, 0, 26, '2012-09-20 17:00:00', 3),\n(3303, 0, 5, '2012-09-20 18:30:00', 3),\n(3304, 1, 11, '2012-09-20 08:30:00', 3),\n(3305, 1, 10, '2012-09-20 10:30:00', 3),\n(3306, 1, 30, '2012-09-20 12:30:00', 3),\n(3307, 1, 10, '2012-09-20 14:00:00', 3),\n(3308, 1, 9, '2012-09-20 16:00:00', 3),\n(3309, 1, 0, '2012-09-20 17:30:00', 3),\n(3310, 1, 24, '2012-09-20 19:00:00', 3),\n(3311, 2, 21, '2012-09-20 08:00:00', 6),\n(3312, 2, 0, '2012-09-20 11:00:00', 3),\n(3313, 2, 14, '2012-09-20 12:30:00', 3),\n(3314, 2, 1, '2012-09-20 14:00:00', 3),\n(3315, 2, 2, '2012-09-20 15:30:00', 3),\n(3316, 2, 14, '2012-09-20 17:30:00', 3),\n(3317, 3, 1, '2012-09-20 09:30:00', 2),\n(3318, 3, 21, '2012-09-20 15:00:00', 2),\n(3319, 3, 30, '2012-09-20 18:30:00', 4),\n(3320, 4, 0, '2012-09-20 08:00:00', 2),\n(3321, 4, 14, '2012-09-20 09:30:00', 2),\n(3322, 4, 0, '2012-09-20 10:30:00', 4),\n(3323, 4, 3, '2012-09-20 12:30:00', 4),\n(3324, 4, 0, '2012-09-20 15:00:00', 2),\n(3325, 4, 0, '2012-09-20 16:30:00', 2),\n(3326, 4, 20, '2012-09-20 18:00:00', 2),\n(3327, 4, 8, '2012-09-20 19:30:00', 2),\n(3328, 5, 0, '2012-09-20 11:00:00', 2),\n(3329, 5, 0, '2012-09-20 13:30:00', 2),\n(3330, 5, 0, '2012-09-20 16:30:00', 2),\n(3331, 5, 0, '2012-09-20 18:30:00', 2),\n(3332, 6, 6, '2012-09-20 08:00:00', 2),\n(3333, 6, 0, '2012-09-20 09:30:00', 6),\n(3334, 6, 0, '2012-09-20 14:00:00', 2),\n(3335, 6, 28, '2012-09-20 15:30:00', 2),\n(3336, 7, 0, '2012-09-20 08:30:00', 2),\n(3337, 7, 24, '2012-09-20 09:30:00', 2),\n(3338, 7, 9, '2012-09-20 13:00:00', 2),\n(3339, 7, 8, '2012-09-20 14:00:00', 2),\n(3340, 7, 4, '2012-09-20 15:00:00', 2),\n(3341, 7, 22, '2012-09-20 16:00:00', 2),\n(3342, 7, 15, '2012-09-20 17:30:00', 2),\n(3343, 7, 8, '2012-09-20 18:30:00', 2),\n(3344, 7, 33, '2012-09-20 19:30:00', 2),\n(3345, 8, 33, '2012-09-20 08:00:00', 1),\n(3346, 8, 24, '2012-09-20 08:30:00', 1),\n(3347, 8, 20, '2012-09-20 09:00:00', 1),\n(3348, 8, 3, '2012-09-20 10:00:00', 1),\n(3349, 8, 20, '2012-09-20 10:30:00', 1),\n(3350, 8, 24, '2012-09-20 11:00:00', 1),\n(3351, 8, 28, '2012-09-20 11:30:00', 1),\n(3352, 8, 3, '2012-09-20 12:00:00', 1),\n(3353, 8, 33, '2012-09-20 12:30:00', 1),\n(3354, 8, 16, '2012-09-20 13:00:00', 1),\n(3355, 8, 21, '2012-09-20 13:30:00', 1),\n(3356, 8, 28, '2012-09-20 14:00:00', 1),\n(3357, 8, 3, '2012-09-20 16:00:00', 1),\n(3358, 8, 0, '2012-09-20 19:00:00', 2),\n(3359, 8, 16, '2012-09-20 20:00:00', 1),\n(3360, 0, 26, '2012-09-21 08:00:00', 3),\n(3361, 0, 11, '2012-09-21 09:30:00', 3),\n(3362, 0, 22, '2012-09-21 12:00:00', 3),\n(3363, 0, 16, '2012-09-21 13:30:00', 3),\n(3364, 0, 5, '2012-09-21 15:30:00', 3),\n(3365, 0, 17, '2012-09-21 17:00:00', 6),\n(3366, 1, 12, '2012-09-21 08:00:00', 3),\n(3367, 1, 16, '2012-09-21 10:00:00', 3),\n(3368, 1, 1, '2012-09-21 11:30:00', 3),\n(3369, 1, 9, '2012-09-21 14:00:00', 3),\n(3370, 1, 10, '2012-09-21 16:00:00', 3),\n(3371, 1, 27, '2012-09-21 18:00:00', 3),\n(3372, 2, 9, '2012-09-21 09:00:00', 3),\n(3373, 2, 21, '2012-09-21 10:30:00', 3),\n(3374, 2, 9, '2012-09-21 12:00:00', 3),\n(3375, 2, 0, '2012-09-21 14:00:00', 6),\n(3376, 3, 29, '2012-09-21 09:00:00', 2),\n(3377, 3, 30, '2012-09-21 10:00:00', 2),\n(3378, 3, 2, '2012-09-21 11:00:00', 2),\n(3379, 3, 20, '2012-09-21 13:00:00', 2),\n(3380, 3, 21, '2012-09-21 14:00:00', 2),\n(3381, 3, 4, '2012-09-21 15:30:00', 2),\n(3382, 3, 30, '2012-09-21 16:30:00', 2),\n(3383, 3, 29, '2012-09-21 18:30:00', 2),\n(3384, 3, 1, '2012-09-21 19:30:00', 2),\n(3385, 4, 16, '2012-09-21 08:30:00', 2),\n(3386, 4, 14, '2012-09-21 09:30:00', 2),\n(3387, 4, 0, '2012-09-21 10:30:00', 2),\n(3388, 4, 8, '2012-09-21 11:30:00', 2),\n(3389, 4, 0, '2012-09-21 13:00:00', 2),\n(3390, 4, 14, '2012-09-21 14:30:00', 2),\n(3391, 4, 0, '2012-09-21 15:30:00', 2),\n(3392, 4, 9, '2012-09-21 16:30:00', 2),\n(3393, 4, 1, '2012-09-21 17:30:00', 2),\n(3394, 4, 3, '2012-09-21 18:30:00', 2),\n(3395, 4, 20, '2012-09-21 19:30:00', 2),\n(3396, 5, 15, '2012-09-21 12:00:00', 2),\n(3397, 5, 0, '2012-09-21 17:30:00', 2),\n(3398, 6, 0, '2012-09-21 09:30:00', 2),\n(3399, 6, 13, '2012-09-21 10:30:00', 2),\n(3400, 6, 0, '2012-09-21 11:30:00', 4),\n(3401, 6, 0, '2012-09-21 14:00:00', 2),\n(3402, 6, 12, '2012-09-21 15:30:00', 2),\n(3403, 6, 12, '2012-09-21 17:30:00', 2),\n(3404, 6, 14, '2012-09-21 18:30:00', 4),\n(3405, 7, 21, '2012-09-21 08:30:00', 2),\n(3406, 7, 5, '2012-09-21 09:30:00', 2),\n(3407, 7, 10, '2012-09-21 11:30:00', 2),\n(3408, 7, 24, '2012-09-21 13:00:00', 2),\n(3409, 7, 4, '2012-09-21 14:30:00', 2),\n(3410, 7, 24, '2012-09-21 16:00:00', 2),\n(3411, 7, 13, '2012-09-21 17:00:00', 2),\n(3412, 7, 5, '2012-09-21 19:00:00', 2),\n(3413, 8, 33, '2012-09-21 08:30:00', 2),\n(3414, 8, 21, '2012-09-21 09:30:00', 2),\n(3415, 8, 28, '2012-09-21 10:30:00', 1),\n(3416, 8, 3, '2012-09-21 11:00:00', 1),\n(3417, 8, 29, '2012-09-21 12:30:00', 1),\n(3418, 8, 12, '2012-09-21 13:00:00', 1),\n(3419, 8, 28, '2012-09-21 14:00:00', 1),\n(3420, 8, 29, '2012-09-21 14:30:00', 1),\n(3421, 8, 6, '2012-09-21 15:00:00', 1),\n(3422, 8, 3, '2012-09-21 16:00:00', 1),\n(3423, 8, 15, '2012-09-21 16:30:00', 1),\n(3424, 8, 8, '2012-09-21 17:00:00', 1),\n(3425, 8, 29, '2012-09-21 18:00:00', 1),\n(3426, 8, 33, '2012-09-21 18:30:00', 1),\n(3427, 8, 30, '2012-09-21 19:00:00', 1),\n(3428, 8, 3, '2012-09-21 19:30:00', 1),\n(3429, 0, 0, '2012-09-22 08:30:00', 3),\n(3430, 0, 11, '2012-09-22 10:00:00', 6),\n(3431, 0, 0, '2012-09-22 13:00:00', 3),\n(3432, 0, 6, '2012-09-22 15:00:00', 3),\n(3433, 0, 14, '2012-09-22 16:30:00', 3),\n(3434, 0, 10, '2012-09-22 18:00:00', 3),\n(3435, 1, 5, '2012-09-22 09:00:00', 3),\n(3436, 1, 10, '2012-09-22 11:00:00', 3),\n(3437, 1, 8, '2012-09-22 12:30:00', 3),\n(3438, 1, 15, '2012-09-22 14:00:00', 3),\n(3439, 1, 12, '2012-09-22 16:00:00', 3),\n(3440, 1, 0, '2012-09-22 17:30:00', 3),\n(3441, 2, 24, '2012-09-22 08:00:00', 3),\n(3442, 2, 0, '2012-09-22 09:30:00', 3),\n(3443, 2, 1, '2012-09-22 11:30:00', 3),\n(3444, 2, 2, '2012-09-22 13:30:00', 3),\n(3445, 2, 1, '2012-09-22 15:30:00', 3),\n(3446, 2, 24, '2012-09-22 17:00:00', 3),\n(3447, 3, 29, '2012-09-22 08:30:00', 2),\n(3448, 3, 20, '2012-09-22 11:00:00', 2),\n(3449, 3, 21, '2012-09-22 13:00:00', 2),\n(3450, 3, 30, '2012-09-22 16:30:00', 2),\n(3451, 3, 22, '2012-09-22 18:30:00', 4),\n(3452, 4, 0, '2012-09-22 08:00:00', 2),\n(3453, 4, 14, '2012-09-22 09:00:00', 2),\n(3454, 4, 7, '2012-09-22 10:00:00', 2),\n(3455, 4, 0, '2012-09-22 11:00:00', 2),\n(3456, 4, 24, '2012-09-22 12:00:00', 2),\n(3457, 4, 0, '2012-09-22 13:00:00', 2),\n(3458, 4, 16, '2012-09-22 14:00:00', 2),\n(3459, 4, 0, '2012-09-22 15:00:00', 8),\n(3460, 4, 2, '2012-09-22 19:00:00', 2),\n(3461, 5, 0, '2012-09-22 12:30:00', 2),\n(3462, 6, 6, '2012-09-22 09:00:00', 2),\n(3463, 6, 0, '2012-09-22 10:30:00', 2),\n(3464, 6, 4, '2012-09-22 11:30:00', 2),\n(3465, 6, 12, '2012-09-22 12:30:00', 2),\n(3466, 6, 0, '2012-09-22 13:30:00', 2),\n(3467, 6, 8, '2012-09-22 14:30:00', 2),\n(3468, 6, 0, '2012-09-22 15:30:00', 4),\n(3469, 6, 12, '2012-09-22 17:30:00', 2),\n(3470, 7, 10, '2012-09-22 08:00:00', 2),\n(3471, 7, 4, '2012-09-22 09:30:00', 2),\n(3472, 7, 27, '2012-09-22 11:00:00', 2),\n(3473, 7, 4, '2012-09-22 13:00:00', 2),\n(3474, 7, 30, '2012-09-22 15:00:00', 2),\n(3475, 7, 33, '2012-09-22 16:00:00', 2),\n(3476, 7, 17, '2012-09-22 18:00:00', 2),\n(3477, 7, 27, '2012-09-22 19:00:00', 2),\n(3478, 8, 22, '2012-09-22 08:00:00', 1),\n(3479, 8, 28, '2012-09-22 08:30:00', 1),\n(3480, 8, 17, '2012-09-22 09:30:00', 1),\n(3481, 8, 21, '2012-09-22 10:00:00', 1),\n(3482, 8, 21, '2012-09-22 11:30:00', 1),\n(3483, 8, 22, '2012-09-22 12:00:00', 1),\n(3484, 8, 29, '2012-09-22 13:30:00', 1),\n(3485, 8, 24, '2012-09-22 15:30:00', 1),\n(3486, 8, 3, '2012-09-22 16:30:00', 1),\n(3487, 8, 28, '2012-09-22 17:00:00', 1),\n(3488, 8, 3, '2012-09-22 18:30:00', 2),\n(3489, 8, 21, '2012-09-22 19:30:00', 1),\n(3490, 8, 11, '2012-09-22 20:00:00', 1),\n(3491, 0, 7, '2012-09-23 08:00:00', 3),\n(3492, 0, 10, '2012-09-23 09:30:00', 3),\n(3493, 0, 15, '2012-09-23 11:00:00', 3),\n(3494, 0, 26, '2012-09-23 12:30:00', 3),\n(3495, 0, 35, '2012-09-23 14:00:00', 3),\n(3496, 0, 2, '2012-09-23 16:30:00', 3),\n(3497, 0, 17, '2012-09-23 18:00:00', 3),\n(3498, 1, 0, '2012-09-23 08:30:00', 3),\n(3499, 1, 24, '2012-09-23 10:30:00', 3),\n(3500, 1, 8, '2012-09-23 12:00:00', 3),\n(3501, 1, 24, '2012-09-23 13:30:00', 3),\n(3502, 1, 15, '2012-09-23 15:00:00', 3),\n(3503, 1, 35, '2012-09-23 16:30:00', 3),\n(3504, 1, 0, '2012-09-23 18:00:00', 3),\n(3505, 2, 1, '2012-09-23 08:00:00', 3),\n(3506, 2, 1, '2012-09-23 11:00:00', 3),\n(3507, 2, 16, '2012-09-23 12:30:00', 3),\n(3508, 2, 29, '2012-09-23 14:00:00', 3),\n(3509, 2, 9, '2012-09-23 15:30:00', 3),\n(3510, 2, 7, '2012-09-23 17:00:00', 3),\n(3511, 3, 3, '2012-09-23 08:00:00', 2),\n(3512, 3, 22, '2012-09-23 09:30:00', 2),\n(3513, 3, 3, '2012-09-23 10:30:00', 2),\n(3514, 3, 22, '2012-09-23 12:30:00', 2),\n(3515, 3, 15, '2012-09-23 13:30:00', 2),\n(3516, 3, 22, '2012-09-23 15:00:00', 2),\n(3517, 3, 3, '2012-09-23 16:00:00', 2),\n(3518, 3, 22, '2012-09-23 17:30:00', 2),\n(3519, 3, 10, '2012-09-23 19:00:00', 2),\n(3520, 4, 11, '2012-09-23 08:00:00', 2),\n(3521, 4, 16, '2012-09-23 09:30:00', 2),\n(3522, 4, 9, '2012-09-23 10:30:00', 2),\n(3523, 4, 0, '2012-09-23 11:30:00', 2),\n(3524, 4, 6, '2012-09-23 12:30:00', 2),\n(3525, 4, 13, '2012-09-23 13:30:00', 4),\n(3526, 4, 22, '2012-09-23 16:00:00', 2),\n(3527, 4, 0, '2012-09-23 17:00:00', 2),\n(3528, 4, 11, '2012-09-23 18:00:00', 2),\n(3529, 4, 1, '2012-09-23 19:00:00', 2),\n(3530, 5, 0, '2012-09-23 15:00:00', 2),\n(3531, 6, 0, '2012-09-23 08:00:00', 4),\n(3532, 6, 0, '2012-09-23 10:30:00', 2),\n(3533, 6, 0, '2012-09-23 12:00:00', 2),\n(3534, 6, 0, '2012-09-23 13:30:00', 6),\n(3535, 6, 12, '2012-09-23 16:30:00', 2),\n(3536, 6, 15, '2012-09-23 17:30:00', 2),\n(3537, 6, 0, '2012-09-23 19:00:00', 2),\n(3538, 7, 8, '2012-09-23 09:00:00', 2),\n(3539, 7, 17, '2012-09-23 10:00:00', 2),\n(3540, 7, 9, '2012-09-23 11:30:00', 2),\n(3541, 7, 27, '2012-09-23 15:00:00', 2),\n(3542, 7, 15, '2012-09-23 16:30:00', 2),\n(3543, 7, 14, '2012-09-23 17:30:00', 2),\n(3544, 7, 0, '2012-09-23 19:00:00', 2),\n(3545, 8, 33, '2012-09-23 08:00:00', 1),\n(3546, 8, 28, '2012-09-23 08:30:00', 1),\n(3547, 8, 0, '2012-09-23 09:00:00', 1),\n(3548, 8, 3, '2012-09-23 09:30:00', 1),\n(3549, 8, 0, '2012-09-23 10:00:00', 1),\n(3550, 8, 29, '2012-09-23 10:30:00', 1),\n(3551, 8, 3, '2012-09-23 11:30:00', 1),\n(3552, 8, 30, '2012-09-23 12:00:00', 1),\n(3553, 8, 21, '2012-09-23 13:00:00', 2),\n(3554, 8, 0, '2012-09-23 15:00:00', 1),\n(3555, 8, 16, '2012-09-23 15:30:00', 1),\n(3556, 8, 13, '2012-09-23 16:00:00', 1),\n(3557, 8, 6, '2012-09-23 16:30:00', 1),\n(3558, 8, 29, '2012-09-23 17:00:00', 1),\n(3559, 8, 28, '2012-09-23 17:30:00', 1),\n(3560, 8, 6, '2012-09-23 18:00:00', 1),\n(3561, 8, 28, '2012-09-23 19:00:00', 1),\n(3562, 8, 29, '2012-09-23 19:30:00', 1),\n(3563, 0, 0, '2012-09-24 08:00:00', 9),\n(3564, 0, 35, '2012-09-24 12:30:00', 3),\n(3565, 0, 0, '2012-09-24 14:00:00', 3),\n(3566, 0, 14, '2012-09-24 15:30:00', 6),\n(3567, 0, 11, '2012-09-24 18:30:00', 3),\n(3568, 1, 28, '2012-09-24 08:00:00', 3),\n(3569, 1, 10, '2012-09-24 09:30:00', 3),\n(3570, 1, 10, '2012-09-24 12:00:00', 6),\n(3571, 1, 16, '2012-09-24 15:00:00', 3),\n(3572, 1, 0, '2012-09-24 16:30:00', 3),\n(3573, 1, 24, '2012-09-24 19:00:00', 3),\n(3574, 2, 21, '2012-09-24 08:00:00', 3),\n(3575, 2, 0, '2012-09-24 09:30:00', 3),\n(3576, 2, 1, '2012-09-24 11:30:00', 3),\n(3577, 2, 3, '2012-09-24 13:00:00', 3),\n(3578, 2, 12, '2012-09-24 14:30:00', 3),\n(3579, 2, 7, '2012-09-24 16:30:00', 3),\n(3580, 2, 3, '2012-09-24 18:00:00', 3),\n(3581, 3, 0, '2012-09-24 08:00:00', 2),\n(3582, 3, 21, '2012-09-24 09:30:00', 2),\n(3583, 3, 16, '2012-09-24 12:00:00', 2),\n(3584, 3, 15, '2012-09-24 13:00:00', 2),\n(3585, 3, 2, '2012-09-24 14:30:00', 2),\n(3586, 3, 20, '2012-09-24 15:30:00', 2),\n(3587, 3, 22, '2012-09-24 17:00:00', 2),\n(3588, 3, 16, '2012-09-24 18:00:00', 2),\n(3589, 3, 0, '2012-09-24 19:00:00', 2),\n(3590, 4, 11, '2012-09-24 08:00:00', 2),\n(3591, 4, 14, '2012-09-24 09:00:00', 2),\n(3592, 4, 0, '2012-09-24 10:30:00', 2),\n(3593, 4, 0, '2012-09-24 12:00:00', 2),\n(3594, 4, 20, '2012-09-24 13:00:00', 2),\n(3595, 4, 0, '2012-09-24 14:00:00', 2),\n(3596, 4, 8, '2012-09-24 15:00:00', 2),\n(3597, 4, 6, '2012-09-24 16:00:00', 2),\n(3598, 4, 8, '2012-09-24 17:00:00', 2),\n(3599, 4, 35, '2012-09-24 18:00:00', 2),\n(3600, 4, 0, '2012-09-24 19:00:00', 2),\n(3601, 5, 14, '2012-09-24 11:00:00', 2),\n(3602, 5, 0, '2012-09-24 14:00:00', 2),\n(3603, 6, 0, '2012-09-24 08:00:00', 2),\n(3604, 6, 9, '2012-09-24 09:30:00', 2),\n(3605, 6, 0, '2012-09-24 10:30:00', 2),\n(3606, 6, 0, '2012-09-24 12:00:00', 4),\n(3607, 6, 14, '2012-09-24 14:00:00', 2),\n(3608, 6, 0, '2012-09-24 15:00:00', 4),\n(3609, 6, 12, '2012-09-24 17:00:00', 2),\n(3610, 6, 33, '2012-09-24 18:00:00', 2),\n(3611, 6, 17, '2012-09-24 19:00:00', 2),\n(3612, 7, 24, '2012-09-24 08:30:00', 2),\n(3613, 7, 22, '2012-09-24 13:00:00', 2),\n(3614, 7, 9, '2012-09-24 14:30:00', 2),\n(3615, 7, 17, '2012-09-24 15:30:00', 2),\n(3616, 7, 28, '2012-09-24 16:30:00', 2),\n(3617, 7, 10, '2012-09-24 17:30:00', 2),\n(3618, 7, 8, '2012-09-24 19:00:00', 2),\n(3619, 8, 0, '2012-09-24 08:00:00', 1),\n(3620, 8, 29, '2012-09-24 08:30:00', 1),\n(3621, 8, 0, '2012-09-24 09:00:00', 1),\n(3622, 8, 3, '2012-09-24 09:30:00', 2),\n(3623, 8, 8, '2012-09-24 10:30:00', 1),\n(3624, 8, 0, '2012-09-24 12:00:00', 1),\n(3625, 8, 28, '2012-09-24 12:30:00', 1),\n(3626, 8, 21, '2012-09-24 13:30:00', 1),\n(3627, 8, 2, '2012-09-24 14:00:00', 1),\n(3628, 8, 3, '2012-09-24 14:30:00', 1),\n(3629, 8, 2, '2012-09-24 16:00:00', 1),\n(3630, 8, 22, '2012-09-24 16:30:00', 1),\n(3631, 8, 0, '2012-09-24 17:00:00', 1),\n(3632, 8, 29, '2012-09-24 17:30:00', 1),\n(3633, 8, 16, '2012-09-24 19:00:00', 1),\n(3634, 8, 29, '2012-09-24 19:30:00', 1),\n(3635, 0, 12, '2012-09-25 08:00:00', 3),\n(3636, 0, 2, '2012-09-25 09:30:00', 6),\n(3637, 0, 16, '2012-09-25 13:00:00', 3),\n(3638, 0, 0, '2012-09-25 15:00:00', 6),\n(3639, 0, 11, '2012-09-25 18:00:00', 3),\n(3640, 1, 9, '2012-09-25 08:00:00', 3),\n(3641, 1, 12, '2012-09-25 10:00:00', 3),\n(3642, 1, 0, '2012-09-25 11:30:00', 3),\n(3643, 1, 12, '2012-09-25 13:00:00', 3),\n(3644, 1, 11, '2012-09-25 14:30:00', 3),\n(3645, 1, 35, '2012-09-25 16:30:00', 3),\n(3646, 1, 0, '2012-09-25 18:30:00', 3),\n(3647, 2, 29, '2012-09-25 08:00:00', 6),\n(3648, 2, 11, '2012-09-25 11:30:00', 3),\n(3649, 2, 13, '2012-09-25 14:30:00', 3),\n(3650, 2, 0, '2012-09-25 16:00:00', 3),\n(3651, 2, 33, '2012-09-25 17:30:00', 3),\n(3652, 3, 20, '2012-09-25 08:00:00', 2),\n(3653, 3, 17, '2012-09-25 11:00:00', 2),\n(3654, 3, 22, '2012-09-25 12:30:00', 2),\n(3655, 3, 35, '2012-09-25 13:30:00', 2),\n(3656, 3, 20, '2012-09-25 17:30:00', 2),\n(3657, 3, 17, '2012-09-25 19:00:00', 2),\n(3658, 4, 8, '2012-09-25 08:00:00', 2),\n(3659, 4, 20, '2012-09-25 09:00:00', 2),\n(3660, 4, 14, '2012-09-25 10:00:00', 2),\n(3661, 4, 0, '2012-09-25 11:30:00', 4),\n(3662, 4, 8, '2012-09-25 13:30:00', 2),\n(3663, 4, 3, '2012-09-25 14:30:00', 2),\n(3664, 4, 0, '2012-09-25 15:30:00', 2),\n(3665, 4, 20, '2012-09-25 16:30:00', 2),\n(3666, 4, 4, '2012-09-25 17:30:00', 2),\n(3667, 4, 6, '2012-09-25 18:30:00', 2),\n(3668, 4, 3, '2012-09-25 19:30:00', 2),\n(3669, 5, 0, '2012-09-25 08:30:00', 2),\n(3670, 5, 0, '2012-09-25 11:30:00', 2),\n(3671, 5, 0, '2012-09-25 16:00:00', 2),\n(3672, 6, 0, '2012-09-25 08:00:00', 4),\n(3673, 6, 8, '2012-09-25 10:00:00', 2),\n(3674, 6, 0, '2012-09-25 11:00:00', 2),\n(3675, 6, 2, '2012-09-25 12:30:00', 2),\n(3676, 6, 0, '2012-09-25 14:00:00', 2),\n(3677, 6, 0, '2012-09-25 15:30:00', 4),\n(3678, 7, 21, '2012-09-25 09:30:00', 2),\n(3679, 7, 10, '2012-09-25 12:30:00', 2),\n(3680, 7, 0, '2012-09-25 13:30:00', 2),\n(3681, 7, 7, '2012-09-25 14:30:00', 2),\n(3682, 7, 33, '2012-09-25 15:30:00', 4),\n(3683, 7, 2, '2012-09-25 18:30:00', 2),\n(3684, 8, 15, '2012-09-25 08:00:00', 1),\n(3685, 8, 21, '2012-09-25 09:00:00', 1),\n(3686, 8, 3, '2012-09-25 09:30:00', 1),\n(3687, 8, 29, '2012-09-25 12:30:00', 1),\n(3688, 8, 3, '2012-09-25 13:30:00', 1),\n(3689, 8, 29, '2012-09-25 14:00:00', 1),\n(3690, 8, 16, '2012-09-25 15:00:00', 1),\n(3691, 8, 28, '2012-09-25 15:30:00', 1),\n(3692, 8, 28, '2012-09-25 17:00:00', 1),\n(3693, 8, 21, '2012-09-25 17:30:00', 1),\n(3694, 8, 3, '2012-09-25 18:30:00', 1),\n(3695, 8, 16, '2012-09-25 19:00:00', 1),\n(3696, 8, 33, '2012-09-25 19:30:00', 1),\n(3697, 0, 5, '2012-09-26 08:00:00', 3),\n(3698, 0, 0, '2012-09-26 09:30:00', 3),\n(3699, 0, 6, '2012-09-26 11:00:00', 3),\n(3700, 0, 11, '2012-09-26 13:30:00', 3),\n(3701, 0, 0, '2012-09-26 15:00:00', 6),\n(3702, 0, 22, '2012-09-26 18:00:00', 3),\n(3703, 1, 0, '2012-09-26 08:00:00', 3),\n(3704, 1, 0, '2012-09-26 10:30:00', 3),\n(3705, 1, 9, '2012-09-26 12:00:00', 3),\n(3706, 1, 35, '2012-09-26 14:00:00', 3),\n(3707, 1, 12, '2012-09-26 15:30:00', 6),\n(3708, 1, 11, '2012-09-26 18:30:00', 3),\n(3709, 2, 1, '2012-09-26 08:00:00', 3),\n(3710, 2, 2, '2012-09-26 09:30:00', 3),\n(3711, 2, 10, '2012-09-26 11:00:00', 3),\n(3712, 2, 1, '2012-09-26 12:30:00', 3),\n(3713, 2, 0, '2012-09-26 14:00:00', 3),\n(3714, 2, 1, '2012-09-26 15:30:00', 3),\n(3715, 2, 17, '2012-09-26 17:00:00', 3),\n(3716, 2, 12, '2012-09-26 18:30:00', 3),\n(3717, 3, 15, '2012-09-26 11:00:00', 2),\n(3718, 3, 30, '2012-09-26 12:00:00', 2),\n(3719, 3, 3, '2012-09-26 15:00:00', 2),\n(3720, 3, 22, '2012-09-26 16:00:00', 2),\n(3721, 3, 15, '2012-09-26 18:30:00', 2),\n(3722, 3, 9, '2012-09-26 19:30:00', 2),\n(3723, 4, 3, '2012-09-26 08:00:00', 2),\n(3724, 4, 0, '2012-09-26 09:00:00', 2),\n(3725, 4, 33, '2012-09-26 10:00:00', 2),\n(3726, 4, 20, '2012-09-26 11:00:00', 4),\n(3727, 4, 29, '2012-09-26 13:00:00', 2),\n(3728, 4, 0, '2012-09-26 14:00:00', 2),\n(3729, 4, 5, '2012-09-26 15:30:00', 2),\n(3730, 4, 0, '2012-09-26 16:30:00', 2),\n(3731, 4, 14, '2012-09-26 17:30:00', 2),\n(3732, 4, 20, '2012-09-26 18:30:00', 2),\n(3733, 5, 0, '2012-09-26 18:30:00', 2),\n(3734, 6, 0, '2012-09-26 08:00:00', 2),\n(3735, 6, 0, '2012-09-26 09:30:00', 2),\n(3736, 6, 30, '2012-09-26 10:30:00', 2),\n(3737, 6, 0, '2012-09-26 11:30:00', 2),\n(3738, 6, 0, '2012-09-26 13:00:00', 8),\n(3739, 6, 10, '2012-09-26 17:00:00', 2),\n(3740, 6, 21, '2012-09-26 18:00:00', 2),\n(3741, 6, 0, '2012-09-26 19:00:00', 2),\n(3742, 7, 7, '2012-09-26 09:00:00', 2),\n(3743, 7, 24, '2012-09-26 10:30:00', 2),\n(3744, 7, 5, '2012-09-26 11:30:00', 2),\n(3745, 7, 27, '2012-09-26 14:30:00', 2),\n(3746, 7, 24, '2012-09-26 16:00:00', 2),\n(3747, 7, 5, '2012-09-26 17:30:00', 2),\n(3748, 8, 0, '2012-09-26 08:30:00', 1),\n(3749, 8, 30, '2012-09-26 09:00:00', 1),\n(3750, 8, 16, '2012-09-26 09:30:00', 1),\n(3751, 8, 21, '2012-09-26 10:00:00', 1),\n(3752, 8, 29, '2012-09-26 10:30:00', 1),\n(3753, 8, 16, '2012-09-26 11:30:00', 1),\n(3754, 8, 29, '2012-09-26 12:00:00', 2),\n(3755, 8, 28, '2012-09-26 13:00:00', 1),\n(3756, 8, 3, '2012-09-26 14:00:00', 2),\n(3757, 8, 20, '2012-09-26 15:30:00', 1),\n(3758, 8, 3, '2012-09-26 16:00:00', 1),\n(3759, 8, 28, '2012-09-26 17:00:00', 1),\n(3760, 8, 21, '2012-09-26 19:00:00', 1),\n(3761, 8, 29, '2012-09-26 19:30:00', 1),\n(3762, 8, 24, '2012-09-26 20:00:00', 1),\n(3763, 0, 11, '2012-09-27 09:00:00', 3),\n(3764, 0, 6, '2012-09-27 11:00:00', 3),\n(3765, 0, 17, '2012-09-27 13:00:00', 3),\n(3766, 0, 26, '2012-09-27 16:00:00', 3),\n(3767, 0, 0, '2012-09-27 17:30:00', 6),\n(3768, 1, 0, '2012-09-27 08:00:00', 9),\n(3769, 1, 8, '2012-09-27 12:30:00', 3),\n(3770, 1, 0, '2012-09-27 14:30:00', 3),\n(3771, 1, 35, '2012-09-27 16:00:00', 3),\n(3772, 1, 10, '2012-09-27 17:30:00', 3),\n(3773, 2, 1, '2012-09-27 08:00:00', 3),\n(3774, 2, 24, '2012-09-27 10:00:00', 3),\n(3775, 2, 36, '2012-09-27 11:30:00', 3),\n(3776, 2, 30, '2012-09-27 15:30:00', 3),\n(3777, 2, 11, '2012-09-27 17:30:00', 3),\n(3778, 2, 2, '2012-09-27 19:00:00', 3),\n(3779, 3, 15, '2012-09-27 08:30:00', 2),\n(3780, 3, 22, '2012-09-27 09:30:00', 2),\n(3781, 3, 0, '2012-09-27 12:00:00', 2),\n(3782, 3, 15, '2012-09-27 13:00:00', 2),\n(3783, 3, 15, '2012-09-27 15:30:00', 2),\n(3784, 3, 20, '2012-09-27 17:30:00', 2),\n(3785, 3, 0, '2012-09-27 18:30:00', 2),\n(3786, 4, 0, '2012-09-27 08:00:00', 2),\n(3787, 4, 24, '2012-09-27 09:00:00', 2),\n(3788, 4, 35, '2012-09-27 10:00:00', 2),\n(3789, 4, 20, '2012-09-27 11:00:00', 2),\n(3790, 4, 0, '2012-09-27 12:00:00', 2),\n(3791, 4, 12, '2012-09-27 13:00:00', 2),\n(3792, 4, 24, '2012-09-27 14:00:00', 2),\n(3793, 4, 36, '2012-09-27 15:00:00', 2),\n(3794, 4, 0, '2012-09-27 16:00:00', 2),\n(3795, 4, 7, '2012-09-27 17:00:00', 2),\n(3796, 4, 35, '2012-09-27 18:00:00', 2),\n(3797, 4, 6, '2012-09-27 19:00:00', 2),\n(3798, 5, 0, '2012-09-27 10:30:00', 2),\n(3799, 5, 22, '2012-09-27 16:30:00', 2),\n(3800, 6, 0, '2012-09-27 08:00:00', 2),\n(3801, 6, 12, '2012-09-27 09:30:00', 2),\n(3802, 6, 0, '2012-09-27 10:30:00', 2),\n(3803, 6, 0, '2012-09-27 12:00:00', 6),\n(3804, 6, 12, '2012-09-27 15:00:00', 4),\n(3805, 6, 0, '2012-09-27 19:00:00', 2),\n(3806, 7, 24, '2012-09-27 08:00:00', 2),\n(3807, 7, 4, '2012-09-27 09:30:00', 2),\n(3808, 7, 10, '2012-09-27 12:00:00', 2),\n(3809, 7, 10, '2012-09-27 13:30:00', 2),\n(3810, 7, 8, '2012-09-27 15:00:00', 2),\n(3811, 7, 9, '2012-09-27 16:00:00', 2),\n(3812, 7, 24, '2012-09-27 18:30:00', 2),\n(3813, 7, 17, '2012-09-27 19:30:00', 2),\n(3814, 8, 28, '2012-09-27 08:00:00', 1),\n(3815, 8, 2, '2012-09-27 08:30:00', 1),\n(3816, 8, 29, '2012-09-27 09:00:00', 1),\n(3817, 8, 33, '2012-09-27 10:30:00', 1),\n(3818, 8, 3, '2012-09-27 11:00:00', 1),\n(3819, 8, 29, '2012-09-27 13:30:00', 1),\n(3820, 8, 22, '2012-09-27 14:00:00', 1),\n(3821, 8, 29, '2012-09-27 15:00:00', 1),\n(3822, 8, 0, '2012-09-27 15:30:00', 1),\n(3823, 8, 20, '2012-09-27 16:00:00', 1),\n(3824, 8, 8, '2012-09-27 16:30:00', 1),\n(3825, 8, 16, '2012-09-27 17:00:00', 1),\n(3826, 8, 27, '2012-09-27 18:00:00', 1),\n(3827, 8, 3, '2012-09-27 19:30:00', 1),\n(3828, 8, 29, '2012-09-27 20:00:00', 1),\n(3829, 0, 35, '2012-09-28 08:30:00', 3),\n(3830, 0, 16, '2012-09-28 10:00:00', 3),\n(3831, 0, 28, '2012-09-28 11:30:00', 3),\n(3832, 0, 0, '2012-09-28 13:00:00', 3),\n(3833, 0, 0, '2012-09-28 15:00:00', 3),\n(3834, 0, 0, '2012-09-28 17:00:00', 3),\n(3835, 1, 10, '2012-09-28 08:00:00', 3),\n(3836, 1, 0, '2012-09-28 09:30:00', 9),\n(3837, 1, 8, '2012-09-28 14:00:00', 3),\n(3838, 1, 0, '2012-09-28 15:30:00', 3),\n(3839, 1, 0, '2012-09-28 17:30:00', 6),\n(3840, 2, 2, '2012-09-28 08:00:00', 3),\n(3841, 2, 21, '2012-09-28 09:30:00', 3),\n(3842, 2, 21, '2012-09-28 11:30:00', 3),\n(3843, 2, 1, '2012-09-28 13:00:00', 3),\n(3844, 2, 5, '2012-09-28 14:30:00', 3),\n(3845, 2, 17, '2012-09-28 16:00:00', 3),\n(3846, 2, 0, '2012-09-28 17:30:00', 3),\n(3847, 2, 1, '2012-09-28 19:00:00', 3),\n(3848, 3, 30, '2012-09-28 09:00:00', 2),\n(3849, 3, 15, '2012-09-28 10:30:00', 2),\n(3850, 3, 13, '2012-09-28 11:30:00', 2),\n(3851, 3, 22, '2012-09-28 12:30:00', 2),\n(3852, 3, 15, '2012-09-28 14:00:00', 2),\n(3853, 3, 24, '2012-09-28 15:30:00', 2),\n(3854, 3, 22, '2012-09-28 17:30:00', 2),\n(3855, 3, 20, '2012-09-28 19:00:00', 2),\n(3856, 4, 24, '2012-09-28 08:00:00', 2),\n(3857, 4, 0, '2012-09-28 09:00:00', 4),\n(3858, 4, 14, '2012-09-28 11:30:00', 2),\n(3859, 4, 0, '2012-09-28 12:30:00', 4),\n(3860, 4, 16, '2012-09-28 14:30:00', 2),\n(3861, 4, 0, '2012-09-28 15:30:00', 2),\n(3862, 4, 5, '2012-09-28 16:30:00', 2),\n(3863, 4, 0, '2012-09-28 17:30:00', 2),\n(3864, 4, 8, '2012-09-28 18:30:00', 2),\n(3865, 4, 7, '2012-09-28 19:30:00', 2),\n(3866, 5, 0, '2012-09-28 10:30:00', 2),\n(3867, 5, 0, '2012-09-28 13:00:00', 2),\n(3868, 5, 11, '2012-09-28 16:00:00', 2),\n(3869, 5, 0, '2012-09-28 17:00:00', 4),\n(3870, 6, 0, '2012-09-28 08:00:00', 6),\n(3871, 6, 12, '2012-09-28 11:00:00', 2),\n(3872, 6, 0, '2012-09-28 12:00:00', 2),\n(3873, 6, 16, '2012-09-28 13:30:00', 2),\n(3874, 6, 0, '2012-09-28 14:30:00', 2),\n(3875, 6, 0, '2012-09-28 16:00:00', 6),\n(3876, 6, 12, '2012-09-28 19:30:00', 2),\n(3877, 7, 17, '2012-09-28 08:00:00', 2),\n(3878, 7, 27, '2012-09-28 09:00:00', 2),\n(3879, 7, 9, '2012-09-28 12:00:00', 2),\n(3880, 7, 21, '2012-09-28 13:30:00', 2),\n(3881, 7, 27, '2012-09-28 15:00:00', 2),\n(3882, 7, 8, '2012-09-28 16:30:00', 2),\n(3883, 7, 6, '2012-09-28 18:00:00', 2),\n(3884, 8, 21, '2012-09-28 08:00:00', 1),\n(3885, 8, 28, '2012-09-28 09:30:00', 1),\n(3886, 8, 3, '2012-09-28 10:30:00', 1),\n(3887, 8, 3, '2012-09-28 12:00:00', 1),\n(3888, 8, 29, '2012-09-28 12:30:00', 1),\n(3889, 8, 28, '2012-09-28 13:00:00', 1),\n(3890, 8, 3, '2012-09-28 13:30:00', 2),\n(3891, 8, 30, '2012-09-28 15:30:00', 1),\n(3892, 8, 12, '2012-09-28 16:30:00', 1),\n(3893, 8, 0, '2012-09-28 17:00:00', 1),\n(3894, 8, 3, '2012-09-28 17:30:00', 1),\n(3895, 8, 29, '2012-09-28 18:00:00', 1),\n(3896, 8, 10, '2012-09-28 18:30:00', 1),\n(3897, 8, 21, '2012-09-28 19:30:00', 1),\n(3898, 8, 16, '2012-09-28 20:00:00', 1),\n(3899, 0, 0, '2012-09-29 08:00:00', 3),\n(3900, 0, 11, '2012-09-29 11:30:00', 6),\n(3901, 0, 6, '2012-09-29 14:30:00', 3),\n(3902, 0, 28, '2012-09-29 16:00:00', 3),\n(3903, 0, 20, '2012-09-29 17:30:00', 3),\n(3904, 1, 0, '2012-09-29 10:00:00', 3),\n(3905, 1, 8, '2012-09-29 11:30:00', 3),\n(3906, 1, 10, '2012-09-29 13:00:00', 3),\n(3907, 1, 12, '2012-09-29 14:30:00', 3),\n(3908, 1, 0, '2012-09-29 16:00:00', 3),\n(3909, 1, 10, '2012-09-29 18:00:00', 3),\n(3910, 2, 1, '2012-09-29 08:00:00', 3),\n(3911, 2, 36, '2012-09-29 09:30:00', 3),\n(3912, 2, 14, '2012-09-29 11:00:00', 3),\n(3913, 2, 21, '2012-09-29 12:30:00', 3),\n(3914, 2, 1, '2012-09-29 14:00:00', 3),\n(3915, 2, 24, '2012-09-29 15:30:00', 3),\n(3916, 2, 12, '2012-09-29 17:00:00', 3),\n(3917, 2, 16, '2012-09-29 18:30:00', 3),\n(3918, 3, 2, '2012-09-29 08:30:00', 2),\n(3919, 3, 21, '2012-09-29 09:30:00', 2),\n(3920, 3, 6, '2012-09-29 11:00:00', 2),\n(3921, 3, 13, '2012-09-29 13:00:00', 2),\n(3922, 3, 16, '2012-09-29 14:00:00', 2),\n(3923, 3, 20, '2012-09-29 16:00:00', 2),\n(3924, 3, 21, '2012-09-29 19:30:00', 2),\n(3925, 4, 16, '2012-09-29 08:00:00', 2),\n(3926, 4, 0, '2012-09-29 09:30:00', 2),\n(3927, 4, 3, '2012-09-29 10:30:00', 2),\n(3928, 4, 20, '2012-09-29 11:30:00', 2),\n(3929, 4, 5, '2012-09-29 12:30:00', 2),\n(3930, 4, 0, '2012-09-29 13:30:00', 2),\n(3931, 4, 3, '2012-09-29 14:30:00', 2),\n(3932, 4, 0, '2012-09-29 15:30:00', 2),\n(3933, 4, 16, '2012-09-29 16:30:00', 2),\n(3934, 4, 13, '2012-09-29 17:30:00', 2),\n(3935, 4, 36, '2012-09-29 18:30:00', 2),\n(3936, 4, 24, '2012-09-29 19:30:00', 2),\n(3937, 5, 0, '2012-09-29 12:30:00', 2),\n(3938, 6, 6, '2012-09-29 08:00:00', 2),\n(3939, 6, 0, '2012-09-29 09:00:00', 4),\n(3940, 6, 24, '2012-09-29 11:00:00', 2),\n(3941, 6, 0, '2012-09-29 12:00:00', 2),\n(3942, 6, 12, '2012-09-29 13:00:00', 2),\n(3943, 6, 0, '2012-09-29 14:00:00', 2),\n(3944, 6, 27, '2012-09-29 17:00:00', 2),\n(3945, 6, 0, '2012-09-29 18:00:00', 4),\n(3946, 7, 8, '2012-09-29 08:30:00', 2),\n(3947, 7, 4, '2012-09-29 10:00:00', 2),\n(3948, 7, 0, '2012-09-29 12:30:00', 2),\n(3949, 7, 24, '2012-09-29 13:30:00', 2),\n(3950, 7, 8, '2012-09-29 14:30:00', 2),\n(3951, 7, 27, '2012-09-29 15:30:00', 2),\n(3952, 7, 8, '2012-09-29 16:30:00', 2),\n(3953, 7, 15, '2012-09-29 18:30:00', 2),\n(3954, 7, 27, '2012-09-29 19:30:00', 2),\n(3955, 8, 12, '2012-09-29 08:00:00', 1),\n(3956, 8, 3, '2012-09-29 08:30:00', 1),\n(3957, 8, 21, '2012-09-29 09:00:00', 1),\n(3958, 8, 29, '2012-09-29 10:00:00', 1),\n(3959, 8, 28, '2012-09-29 10:30:00', 1),\n(3960, 8, 2, '2012-09-29 11:00:00', 2),\n(3961, 8, 29, '2012-09-29 12:00:00', 2),\n(3962, 8, 20, '2012-09-29 13:00:00', 1),\n(3963, 8, 28, '2012-09-29 13:30:00', 1),\n(3964, 8, 3, '2012-09-29 14:00:00', 1),\n(3965, 8, 28, '2012-09-29 14:30:00', 1),\n(3966, 8, 12, '2012-09-29 16:00:00', 1),\n(3967, 8, 26, '2012-09-29 16:30:00', 1),\n(3968, 8, 15, '2012-09-29 17:00:00', 1),\n(3969, 8, 28, '2012-09-29 17:30:00', 1),\n(3970, 8, 29, '2012-09-29 18:00:00', 2),\n(3971, 8, 4, '2012-09-29 19:30:00', 1),\n(3972, 8, 33, '2012-09-29 20:00:00', 1),\n(3973, 0, 4, '2012-09-30 08:00:00', 3),\n(3974, 0, 35, '2012-09-30 09:30:00', 3),\n(3975, 0, 0, '2012-09-30 11:00:00', 6),\n(3976, 0, 36, '2012-09-30 14:00:00', 3),\n(3977, 0, 24, '2012-09-30 16:00:00', 3),\n(3978, 0, 0, '2012-09-30 17:30:00', 3),\n(3979, 0, 24, '2012-09-30 19:00:00', 3),\n(3980, 1, 8, '2012-09-30 08:30:00', 3),\n(3981, 1, 0, '2012-09-30 10:00:00', 3),\n(3982, 1, 10, '2012-09-30 11:30:00', 3),\n(3983, 1, 11, '2012-09-30 13:30:00', 6),\n(3984, 1, 10, '2012-09-30 16:30:00', 3),\n(3985, 1, 8, '2012-09-30 18:30:00', 3),\n(3986, 2, 1, '2012-09-30 08:00:00', 3),\n(3987, 2, 17, '2012-09-30 09:30:00', 3),\n(3988, 2, 29, '2012-09-30 11:00:00', 3),\n(3989, 2, 35, '2012-09-30 12:30:00', 3),\n(3990, 2, 1, '2012-09-30 14:00:00', 6),\n(3991, 2, 5, '2012-09-30 17:00:00', 3),\n(3992, 2, 35, '2012-09-30 18:30:00', 3),\n(3993, 3, 24, '2012-09-30 08:00:00', 2),\n(3994, 3, 3, '2012-09-30 09:30:00', 2),\n(3995, 3, 36, '2012-09-30 10:30:00', 2),\n(3996, 3, 36, '2012-09-30 12:00:00', 2),\n(3997, 3, 0, '2012-09-30 14:30:00', 2),\n(3998, 3, 1, '2012-09-30 18:30:00', 2),\n(3999, 4, 13, '2012-09-30 08:00:00', 2),\n(4000, 4, 16, '2012-09-30 09:00:00', 2),\n(4001, 4, 0, '2012-09-30 10:00:00', 2),\n(4002, 4, 20, '2012-09-30 11:00:00', 2),\n(4003, 4, 4, '2012-09-30 12:30:00', 2),\n(4004, 4, 3, '2012-09-30 13:30:00', 2),\n(4005, 4, 20, '2012-09-30 15:00:00', 2),\n(4006, 4, 0, '2012-09-30 16:00:00', 2),\n(4007, 4, 3, '2012-09-30 17:00:00', 2),\n(4008, 4, 0, '2012-09-30 18:00:00', 2),\n(4009, 5, 0, '2012-09-30 11:30:00', 2),\n(4010, 5, 0, '2012-09-30 19:30:00', 2),\n(4011, 6, 0, '2012-09-30 08:00:00', 2),\n(4012, 6, 27, '2012-09-30 09:30:00', 2),\n(4013, 6, 0, '2012-09-30 11:00:00', 2),\n(4014, 6, 0, '2012-09-30 12:30:00', 2),\n(4015, 6, 12, '2012-09-30 14:00:00', 2),\n(4016, 6, 0, '2012-09-30 15:30:00', 2),\n(4017, 6, 35, '2012-09-30 16:30:00', 2),\n(4018, 6, 0, '2012-09-30 17:30:00', 2),\n(4019, 6, 0, '2012-09-30 19:00:00', 2),\n(4020, 7, 27, '2012-09-30 08:30:00', 2),\n(4021, 7, 33, '2012-09-30 09:30:00', 2),\n(4022, 7, 33, '2012-09-30 11:00:00', 2),\n(4023, 7, 5, '2012-09-30 14:30:00', 2),\n(4024, 7, 15, '2012-09-30 16:30:00', 2),\n(4025, 7, 24, '2012-09-30 17:30:00', 2),\n(4026, 7, 5, '2012-09-30 19:00:00', 2),\n(4027, 8, 16, '2012-09-30 08:00:00', 1),\n(4028, 8, 21, '2012-09-30 08:30:00', 2),\n(4029, 8, 3, '2012-09-30 10:30:00', 1),\n(4030, 8, 16, '2012-09-30 11:00:00', 1),\n(4031, 8, 3, '2012-09-30 11:30:00', 1),\n(4032, 8, 17, '2012-09-30 12:00:00', 1),\n(4033, 8, 21, '2012-09-30 12:30:00', 1),\n(4034, 8, 3, '2012-09-30 13:00:00', 1),\n(4035, 8, 29, '2012-09-30 13:30:00', 1),\n(4036, 8, 28, '2012-09-30 14:30:00', 1),\n(4037, 8, 29, '2012-09-30 15:30:00', 1),\n(4038, 8, 29, '2012-09-30 16:30:00', 2),\n(4039, 8, 29, '2012-09-30 18:00:00', 1),\n(4040, 8, 21, '2012-09-30 18:30:00', 1),\n(4041, 8, 16, '2012-09-30 19:00:00', 1),\n(4042, 8, 29, '2012-09-30 19:30:00', 1),\n(4043, 8, 5, '2013-01-01 15:30:00', 1);\n\n\n--\n-- TOC entry 2200 (class 0 OID 32770)\n-- Dependencies: 169\n-- Data for Name: facilities; Type: TABLE DATA; Schema: cd; Owner: -\n--\n\nINSERT INTO facilities (facid, name, membercost, guestcost, initialoutlay, monthlymaintenance) VALUES\n(0, 'Tennis Court 1', 5, 25, 10000, 200),\n(1, 'Tennis Court 2', 5, 25, 8000, 200),\n(2, 'Badminton Court', 0, 15.5, 4000, 50),\n(3, 'Table Tennis', 0, 5, 320, 10),\n(4, 'Massage Room 1', 35, 80, 4000, 3000),\n(5, 'Massage Room 2', 35, 80, 4000, 3000),\n(6, 'Squash Court', 3.5, 17.5, 5000, 80),\n(7, 'Snooker Table', 0, 5, 450, 15),\n(8, 'Pool Table', 0, 5, 400, 15);\n\n\n--\n-- TOC entry 2201 (class 0 OID 32800)\n-- Dependencies: 170\n-- Data for Name: members; Type: TABLE DATA; Schema: cd; Owner: -\n--\n\nINSERT INTO members (memid, surname, firstname, address, zipcode, telephone, recommendedby, joindate) VALUES\n(0, 'GUEST', 'GUEST', 'GUEST', 0, '(000) 000-0000', NULL, '2012-07-01 00:00:00'),\n(1, 'Smith', 'Darren', '8 Bloomsbury Close, Boston', 4321, '555-555-5555', NULL, '2012-07-02 12:02:05'),\n(2, 'Smith', 'Tracy', '8 Bloomsbury Close, New York', 4321, '555-555-5555', NULL, '2012-07-02 12:08:23'),\n(3, 'Rownam', 'Tim', '23 Highway Way, Boston', 23423, '(844) 693-0723', NULL, '2012-07-03 09:32:15'),\n(4, 'Joplette', 'Janice', '20 Crossing Road, New York', 234, '(833) 942-4710', 1, '2012-07-03 10:25:05'),\n(5, 'Butters', 'Gerald', '1065 Huntingdon Avenue, Boston', 56754, '(844) 078-4130', 1, '2012-07-09 10:44:09'),\n(6, 'Tracy', 'Burton', '3 Tunisia Drive, Boston', 45678, '(822) 354-9973', NULL, '2012-07-15 08:52:55'),\n(7, 'Dare', 'Nancy', '6 Hunting Lodge Way, Boston', 10383, '(833) 776-4001', 4, '2012-07-25 08:59:12'),\n(8, 'Boothe', 'Tim', '3 Bloomsbury Close, Reading, 00234', 234, '(811) 433-2547', 3, '2012-07-25 16:02:35'),\n(9, 'Stibbons', 'Ponder', '5 Dragons Way, Winchester', 87630, '(833) 160-3900', 6, '2012-07-25 17:09:05'),\n(10, 'Owen', 'Charles', '52 Cheshire Grove, Winchester, 28563', 28563, '(855) 542-5251', 1, '2012-08-03 19:42:37'),\n(11, 'Jones', 'David', '976 Gnats Close, Reading', 33862, '(844) 536-8036', 4, '2012-08-06 16:32:55'),\n(12, 'Baker', 'Anne', '55 Powdery Street, Boston', 80743, '844-076-5141', 9, '2012-08-10 14:23:22'),\n(13, 'Farrell', 'Jemima', '103 Firth Avenue, North Reading', 57392, '(855) 016-0163', NULL, '2012-08-10 14:28:01'),\n(14, 'Smith', 'Jack', '252 Binkington Way, Boston', 69302, '(822) 163-3254', 1, '2012-08-10 16:22:05'),\n(15, 'Bader', 'Florence', '264 Ursula Drive, Westford', 84923, '(833) 499-3527', 9, '2012-08-10 17:52:03'),\n(16, 'Baker', 'Timothy', '329 James Street, Reading', 58393, '833-941-0824', 13, '2012-08-15 10:34:25'),\n(17, 'Pinker', 'David', '5 Impreza Road, Boston', 65332, '811 409-6734', 13, '2012-08-16 11:32:47'),\n(20, 'Genting', 'Matthew', '4 Nunnington Place, Wingfield, Boston', 52365, '(811) 972-1377', 5, '2012-08-19 14:55:55'),\n(21, 'Mackenzie', 'Anna', '64 Perkington Lane, Reading', 64577, '(822) 661-2898', 1, '2012-08-26 09:32:05'),\n(22, 'Coplin', 'Joan', '85 Bard Street, Bloomington, Boston', 43533, '(822) 499-2232', 16, '2012-08-29 08:32:41'),\n(24, 'Sarwin', 'Ramnaresh', '12 Bullington Lane, Boston', 65464, '(822) 413-1470', 15, '2012-09-01 08:44:42'),\n(26, 'Jones', 'Douglas', '976 Gnats Close, Reading', 11986, '844 536-8036', 11, '2012-09-02 18:43:05'),\n(27, 'Rumney', 'Henrietta', '3 Burkington Plaza, Boston', 78533, '(822) 989-8876', 20, '2012-09-05 08:42:35'),\n(28, 'Farrell', 'David', '437 Granite Farm Road, Westford', 43532, '(855) 755-9876', NULL, '2012-09-15 08:22:05'),\n(29, 'Worthington-Smyth', 'Henry', '55 Jagbi Way, North Reading', 97676, '(855) 894-3758', 2, '2012-09-17 12:27:15'),\n(30, 'Purview', 'Millicent', '641 Drudgery Close, Burnington, Boston', 34232, '(855) 941-9786', 2, '2012-09-18 19:04:01'),\n(33, 'Tupperware', 'Hyacinth', '33 Cheerful Plaza, Drake Road, Westford', 68666, '(822) 665-5327', NULL, '2012-09-18 19:32:05'),\n(35, 'Hunt', 'John', '5 Bullington Lane, Boston', 54333, '(899) 720-6978', 30, '2012-09-19 11:32:45'),\n(36, 'Crumpet', 'Erica', 'Crimson Road, North Reading', 75655, '(811) 732-4816', 2, '2012-09-22 08:36:38'),\n(37, 'Smith', 'Darren', '3 Funktown, Denzington, Boston', 66796, '(822) 577-3541', NULL, '2012-09-26 18:08:45');\n\n\n--\n-- TOC entry 2196 (class 2606 OID 32822)\n-- Name: bookings_pk; Type: CONSTRAINT; Schema: cd; Owner: -; Tablespace:\n--\n\nALTER TABLE ONLY bookings\n    ADD CONSTRAINT bookings_pk PRIMARY KEY (bookid);\n\n\n--\n-- TOC entry 2192 (class 2606 OID 32777)\n-- Name: facilities_pk; Type: CONSTRAINT; Schema: cd; Owner: -; Tablespace:\n--\n\nALTER TABLE ONLY facilities\n    ADD CONSTRAINT facilities_pk PRIMARY KEY (facid);\n\n\n--\n-- TOC entry 2194 (class 2606 OID 32807)\n-- Name: members_pk; Type: CONSTRAINT; Schema: cd; Owner: -; Tablespace:\n--\n\nALTER TABLE ONLY members\n    ADD CONSTRAINT members_pk PRIMARY KEY (memid);\n\n\n--\n-- TOC entry 2198 (class 2606 OID 32823)\n-- Name: fk_bookings_facid; Type: FK CONSTRAINT; Schema: cd; Owner: -\n--\n\nALTER TABLE ONLY bookings\n    ADD CONSTRAINT fk_bookings_facid FOREIGN KEY (facid) REFERENCES facilities(facid);\n\n\n--\n-- TOC entry 2199 (class 2606 OID 32828)\n-- Name: fk_bookings_memid; Type: FK CONSTRAINT; Schema: cd; Owner: -\n--\n\nALTER TABLE ONLY bookings\n    ADD CONSTRAINT fk_bookings_memid FOREIGN KEY (memid) REFERENCES members(memid);\n\n\n--\n-- TOC entry 2197 (class 2606 OID 32808)\n-- Name: fk_members_recommendedby; Type: FK CONSTRAINT; Schema: cd; Owner: -\n--\n\nALTER TABLE ONLY members\n    ADD CONSTRAINT fk_members_recommendedby FOREIGN KEY (recommendedby) REFERENCES members(memid) ON DELETE SET NULL;\n\n\n-- Completed on 2013-05-19 16:05:12 BST\n\n--\n-- PostgreSQL database dump complete\n--\n\nCREATE INDEX \"bookings.memid_facid\"\n  ON bookings\n  USING btree\n  (memid, facid);\n\nCREATE INDEX \"bookings.facid_memid\"\n  ON bookings\n  USING btree\n  (facid, memid);\n\nCREATE INDEX \"bookings.facid_starttime\"\n  ON bookings\n  USING btree\n  (facid, starttime);\n\nCREATE INDEX \"bookings.memid_starttime\"\n  ON bookings\n  USING btree\n  (memid, starttime);\n\nCREATE INDEX \"bookings.starttime\"\n  ON bookings\n  USING btree\n  (starttime);\n\nCREATE INDEX \"members.joindate\"\n  ON members\n  USING btree\n  (joindate);\n\nCREATE INDEX \"members.recommendedby\"\n  ON members\n  USING btree\n  (recommendedby);\n\nANALYZE;\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# peewee documentation build configuration file, created by\n# sphinx-quickstart on Fri Nov 26 11:05:15 2010.\n#\n# This file is execfile()d with the current directory set to its containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n#RTD_NEW_THEME = True\n\nimport sys, os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#sys.path.insert(0, os.path.abspath('.'))\n\n# -- General configuration -----------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be extensions\n# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.\n#extensions = ['sphinx.ext.autodoc', 'sphinx_rtd_theme']\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = 'peewee'\ncopyright = 'charles leifer'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nsrc_dir = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))\nsys.path.insert(0, src_dir)\nfrom peewee import __version__\nversion = __version__\n# The full version, including alpha/beta/rc tags.\nrelease = __version__\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = ['_build']\n\n# The reST default role (used for this markup: `text`) to use for all documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\nadd_module_names = False\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\n#pygments_style = 'pastie'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n\n# -- Options for HTML output ---------------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#html_theme = 'sphinx_rtd_theme'\n#html_theme = 'bizstyle'\n#html_theme_options = {\n#    'body_max_width': '800px',\n#}\nhtml_theme = 'alabaster'\nhtml_theme_options = {\n    'github_user': 'coleifer',\n    'github_repo': 'peewee',\n    'page_width': '1000px',\n    'sidebar_width': '240px',\n    'font_size': '15px',\n}\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {\n#    'index_logo': 'peewee4-logo.png'\n#}\n\n# Add any paths that contain custom themes here, relative to this directory.\n#html_theme_path = ['_themes']\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'peeweedoc'\n\n\n# -- Options for LaTeX output --------------------------------------------------\n\n# The paper size ('letter' or 'a4').\n#latex_paper_size = 'letter'\n\n# The font size ('10pt', '11pt' or '12pt').\n#latex_font_size = '10pt'\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title, author, documentclass [howto/manual]).\nlatex_documents = [\n  ('index', 'peewee.tex', u'peewee Documentation',\n   u'charles leifer', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Additional stuff for the LaTeX preamble.\n#latex_preamble = ''\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\nautodoc_default_flags = ['members', 'show-inheritance']\nautodoc_member_order = 'bysource'\n\n\n# -- Options for manual page output --------------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'peewee', u'peewee Documentation',\n     [u'charles leifer'], 1)\n]\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. peewee documentation master file, created by\n   sphinx-quickstart on Thu Nov 25 21:20:29 2010.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\npeewee\n======\n\n.. image:: peewee4-logo.png\n\nPeewee is a simple and small ORM. It has few (but expressive) concepts, making\nit easy to learn and intuitive to use.\n\n* a small, expressive ORM\n* flexible query-builder that exposes full power of SQL\n* supports :ref:`sqlite, mysql, mariadb, postgresql <database>`.\n* :ref:`asyncio support <pwasyncio>`\n* tons of extensions\n* use with :ref:`flask <flask>`, :ref:`fastapi <fastapi>`, :ref:`pydantic <pydantic>`\n  and :ref:`more <framework-integration>`\n\nPeewee's source code hosted on `GitHub <https://github.com/coleifer/peewee>`_.\n\nNew to peewee? These may help:\n\n* :ref:`Quickstart <quickstart>`\n* :ref:`Example twitter app <example>`\n* :ref:`Using peewee interactively <interactive>`\n* :ref:`Models and fields <models>`\n* :ref:`Querying <querying>`\n* :ref:`Relationships and joins <relationships>`\n* :ref:`Extensive library of SQL / Peewee examples <query-library>`\n* :ref:`Flask setup <flask>` or :ref:`FastAPI setup <fastapi>`\n\nContents:\n---------\n\n.. toctree::\n   :maxdepth: 2\n\n   peewee/installation\n   peewee/quickstart\n   peewee/example\n   peewee/database\n   peewee/models\n   peewee/relationships\n   peewee/querying\n   peewee/writing\n   peewee/query_operators\n   peewee/transactions\n   peewee/schema\n   peewee/asyncio\n   peewee/framework_integration\n   peewee/interactive\n   peewee/query_builder\n   peewee/query_library\n   peewee/api\n   peewee/sqlite\n   peewee/postgres\n   peewee/mysql\n   peewee/db_tools\n   peewee/orm_utils\n   peewee/recipes\n   peewee/contributing\n\nNote\n----\n\nIf you find any bugs, odd behavior, or have an idea for a new feature please\ndon't hesitate to `open an issue <https://github.com/coleifer/peewee/issues?state=open>`_\non GitHub.\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\n\nREM Command file for Sphinx documentation\n\nif \"%SPHINXBUILD%\" == \"\" (\n\tset SPHINXBUILD=sphinx-build\n)\nset BUILDDIR=_build\nset ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .\nif NOT \"%PAPER%\" == \"\" (\n\tset ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%\n)\n\nif \"%1\" == \"\" goto help\n\nif \"%1\" == \"help\" (\n\t:help\n\techo.Please use `make ^<target^>` where ^<target^> is one of\n\techo.  html       to make standalone HTML files\n\techo.  dirhtml    to make HTML files named index.html in directories\n\techo.  singlehtml to make a single large HTML file\n\techo.  pickle     to make pickle files\n\techo.  json       to make JSON files\n\techo.  htmlhelp   to make HTML files and a HTML help project\n\techo.  qthelp     to make HTML files and a qthelp project\n\techo.  devhelp    to make HTML files and a Devhelp project\n\techo.  epub       to make an epub\n\techo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\n\techo.  text       to make text files\n\techo.  man        to make manual pages\n\techo.  changes    to make an overview over all changed/added/deprecated items\n\techo.  linkcheck  to check all external links for integrity\n\techo.  doctest    to run all doctests embedded in the documentation if enabled\n\tgoto end\n)\n\nif \"%1\" == \"clean\" (\n\tfor /d %%i in (%BUILDDIR%\\*) do rmdir /q /s %%i\n\tdel /q /s %BUILDDIR%\\*\n\tgoto end\n)\n\nif \"%1\" == \"html\" (\n\t%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/html.\n\tgoto end\n)\n\nif \"%1\" == \"dirhtml\" (\n\t%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.\n\tgoto end\n)\n\nif \"%1\" == \"singlehtml\" (\n\t%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml\n\techo.\n\techo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.\n\tgoto end\n)\n\nif \"%1\" == \"pickle\" (\n\t%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle\n\techo.\n\techo.Build finished; now you can process the pickle files.\n\tgoto end\n)\n\nif \"%1\" == \"json\" (\n\t%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json\n\techo.\n\techo.Build finished; now you can process the JSON files.\n\tgoto end\n)\n\nif \"%1\" == \"htmlhelp\" (\n\t%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp\n\techo.\n\techo.Build finished; now you can run HTML Help Workshop with the ^\n.hhp project file in %BUILDDIR%/htmlhelp.\n\tgoto end\n)\n\nif \"%1\" == \"qthelp\" (\n\t%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp\n\techo.\n\techo.Build finished; now you can run \"qcollectiongenerator\" with the ^\n.qhcp project file in %BUILDDIR%/qthelp, like this:\n\techo.^> qcollectiongenerator %BUILDDIR%\\qthelp\\peewee.qhcp\n\techo.To view the help file:\n\techo.^> assistant -collectionFile %BUILDDIR%\\qthelp\\peewee.ghc\n\tgoto end\n)\n\nif \"%1\" == \"devhelp\" (\n\t%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp\n\techo.\n\techo.Build finished.\n\tgoto end\n)\n\nif \"%1\" == \"epub\" (\n\t%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub\n\techo.\n\techo.Build finished. The epub file is in %BUILDDIR%/epub.\n\tgoto end\n)\n\nif \"%1\" == \"latex\" (\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\n\techo.\n\techo.Build finished; the LaTeX files are in %BUILDDIR%/latex.\n\tgoto end\n)\n\nif \"%1\" == \"text\" (\n\t%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text\n\techo.\n\techo.Build finished. The text files are in %BUILDDIR%/text.\n\tgoto end\n)\n\nif \"%1\" == \"man\" (\n\t%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man\n\techo.\n\techo.Build finished. The manual pages are in %BUILDDIR%/man.\n\tgoto end\n)\n\nif \"%1\" == \"changes\" (\n\t%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes\n\techo.\n\techo.The overview file is in %BUILDDIR%/changes.\n\tgoto end\n)\n\nif \"%1\" == \"linkcheck\" (\n\t%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck\n\techo.\n\techo.Link check complete; look for any errors in the above output ^\nor in %BUILDDIR%/linkcheck/output.txt.\n\tgoto end\n)\n\nif \"%1\" == \"doctest\" (\n\t%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest\n\techo.\n\techo.Testing of doctests in the sources finished, look at the ^\nresults in %BUILDDIR%/doctest/output.txt.\n\tgoto end\n)\n\n:end\n"
  },
  {
    "path": "docs/peewee/api.rst",
    "content": ".. _api:\n\nAPI Reference\n=============\n\nThis document specifies Peewee's APIs.\n\n.. contents:: On this page\n   :local:\n   :depth: 1\n\nDatabase\n--------\n\n.. class:: Database(database, thread_safe=True, field_types=None, operations=None, autoconnect=True, **kwargs)\n\n   :param str database: Database name or filename for SQLite (or ``None`` to\n       :ref:`defer initialization <initializing-database>`, in which case\n       you must call :meth:`Database.init`, specifying the database name).\n   :param bool thread_safe: Whether to store connection state in a\n       thread-local.\n   :param dict field_types: A mapping of additional field types to support.\n   :param dict operations: A mapping of additional operations to support.\n   :param bool autoconnect: Automatically connect to database if attempting to\n       execute a query on a closed database.\n   :param kwargs: Arbitrary keyword arguments that will be passed to the\n       database driver when a connection is created, for example ``password``,\n       ``host``, etc.\n\n   The :class:`Database` is responsible for:\n\n   * Executing queries\n   * Managing connections\n   * Transactions\n   * Introspection\n\n   The database can be instantiated with ``None`` as the database name if\n   the database is not known until run-time. In this way you can create a\n   database instance and then configure it elsewhere when the settings are\n   known. This is called :ref:`deferred initialization <initializing-database>`.\n\n   Examples:\n\n   .. code-block:: python\n\n      # Sqlite database using WAL-mode and 64MB page-cache.\n      db = SqliteDatabase('app.db', pragmas={\n          'journal_mode': 'wal',\n          'cache_size': -64000})\n\n      # Postgresql database on remote host.\n      db = PostgresqlDatabase(\n          'my_app',\n          user='postgres',\n          host='10.8.0.3',\n          password='secret')\n\n   Deferred initialization example:\n\n   .. code-block:: python\n\n      db = PostgresqlDatabase(None)\n\n      class BaseModel(Model):\n          class Meta:\n              database = db\n\n      # Read database connection info from env, for example:\n      db_name = os.environ['DATABASE']\n      db_host = os.environ['PGHOST']\n\n      # Initialize database.\n      db.init(db_name, host=db_host, user='postgres')\n\n   .. attribute:: param = '?'\n\n      String used as parameter placeholder in SQL queries.\n\n   .. attribute:: quote = '\"\"'\n\n      Type of quotation-mark(s) to use to denote entities such as tables or\n      columns, specified as ``'<open quote><close quote>'``.\n\n   .. method:: init(database, **kwargs)\n\n      :param str database: Database name or filename for SQLite.\n      :param kwargs: Arbitrary keyword arguments that will be passed to the\n          database driver when a connection is created, for example\n          ``password``, ``host``, etc.\n\n      Initialize a *deferred* database. See :ref:`initializing-database`\n      for more info.\n\n   .. method:: connect(reuse_if_open=False)\n\n      :param bool reuse_if_open: Do not raise an exception if a connection is\n          already opened.\n      :return: whether a new connection was opened.\n      :rtype: bool\n      :raises: ``OperationalError`` if connection already open and\n          ``reuse_if_open`` is not set to ``True``.\n\n      Open a connection to the database.\n\n      .. code-block:: python\n\n         db.connect()\n\n         # Or:\n         db.connect(reuse_if_open=True)\n\n   .. method:: close()\n\n      :return: Whether the connection was closed. If the database was already\n          closed, this returns ``False``.\n      :rtype: bool\n\n      Close the connection to the database.\n\n      .. code-block:: python\n\n         if not db.is_closed():\n             db.close()\n\n   .. method:: is_closed()\n\n      :return: return ``True`` if database is closed, ``False`` if open.\n      :rtype: bool\n\n   .. method:: connection()\n\n      Return the DB-API driver connection. If a connection is not open, one\n      will be opened.\n\n      .. code-block:: python\n\n         db = SqliteDatabase(':memory:')\n\n         # Get the sqlite3.Connection() instance.\n         conn = db.connection()\n\n   .. method:: __enter__()\n   .. method:: __exit__(exc_type, exc_val, exc_tb)\n   .. method:: __call__()\n\n      The database object can be used as a context manager or decorator.\n\n      1. Connection opens when context manager / decorated function is entered.\n      2. Peewee begins a transaction.\n      3. Control is passed to user for duration of block.\n      4. Peewee commits transaction if block exits cleanly, otherwise issues a\n         rollback.\n      5. Peewee closes the connection.\n      6. Any unhandled exception is raised.\n\n      .. code-block:: python\n\n         with db:\n             User.create(username='charlie')\n             # Transaction is committed when the block exits normally,\n             # rolled back if an exception is raised.\n\n      Decorator:\n\n      .. code-block:: python\n\n         @db\n         def demo():\n             print('closed?', db.is_closed())\n\n         demo()  # \"closed? False\"\n         db.is_closed()  # True\n\n   .. method:: connection_context()\n\n      Create a context-manager or decorator that will hold open a connection\n      for the duration of the wrapped block.\n\n      Example:\n\n      .. code-block:: python\n\n         with db.connection_context():\n             # Connection is open; no implicit transaction.\n             results = User.select()\n\n      Decorator:\n\n      .. code-block:: python\n\n         @db.connection_context()\n         def load_fixtures():\n             db.create_tables([User, Tweet])\n             import_data()\n\n   .. method:: cursor(named_cursor=None)\n\n      :param named_cursor: Reserved for internal use by Postgres extension.\n\n      Return a DB-API ``cursor`` object on the current connection. If a\n      connection is not open, one will be opened.\n\n   .. method:: execute_sql(sql, params=None)\n\n      :param str sql: SQL string to execute.\n      :param tuple params: Parameters for query.\n      :return: cursor object.\n\n      Execute a SQL query and return a cursor over the results.\n\n      .. code-block:: python\n\n         db = SqliteDatabase('my_app.db')\n         db.connect()\n\n         # Example of executing a simple query and ignoring the results.\n         db.execute_sql(\"ATTACH DATABASE ':memory:' AS cache;\")\n\n         # Example of iterating over the results of a query using the cursor.\n         cursor = db.execute_sql('SELECT * FROM users WHERE status = ?', (ACTIVE,))\n         for row in cursor.fetchall():\n             # Do something with row, which is a tuple containing column data.\n             pass\n\n   .. method:: execute(query, **context_options)\n\n      :param query: A :class:`Query` instance.\n      :param context_options: Arbitrary options to pass to the SQL generator.\n      :return: cursor object.\n\n      Execute a SQL query by compiling a ``Query`` instance and executing the\n      resulting SQL.\n\n      .. code-block:: python\n\n         query = User.insert({'username': 'Huey'})\n         db.execute(query)  # Equivalent to query.execute()\n\n   .. method:: last_insert_id(cursor, query_type=None)\n\n      :param cursor: cursor object.\n      :return: primary key of last-inserted row.\n\n   .. method:: rows_affected(cursor)\n\n      :param cursor: cursor object.\n      :return: number of rows modified by query.\n\n   .. method:: atomic(...)\n\n      Create a context-manager or decorator which wraps a block of code in a\n      transaction (or savepoint).\n\n      Calls to :meth:`~Database.atomic` can be nested.\n\n      Database-specific parameters:\n\n      :class:`PostgresqlDatabase` and :class:`MySQLDatabase` accept an\n      ``isolation_level`` parameter. :class:`SqliteDatabase` accepts a\n      ``lock_type`` parameter. Refer to :ref:`sqlite-locking` and :ref:`postgres-isolation`\n      for discussion.\n\n      :param str isolation_level: Isolation strategy: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE\n      :param str lock_type: Locking strategy: DEFERRED, IMMEDIATE, EXCLUSIVE.\n\n      Example code::\n\n         with db.atomic() as txn:\n             user = User.create(username='charlie')\n             with db.atomic():\n                 tweet = Tweet.create(user=user, content='Hello')\n\n         # Both rows are committed when block exits normally.\n\n      As a decorator:\n\n      .. code-block:: python\n\n         @db.atomic()\n         def create_user_with_tweet(username, content):\n             user = User.create(username=username)\n             Tweet.create(user=user, content=content)\n             return user\n\n      Transactions (and save-points) can be committed or rolled-back within the\n      wrapped block. If this occurs, a new transaction or savepoint is begun:\n\n      Example:\n\n      .. code-block:: python\n\n          with db.atomic() as txn:\n              User.create(username='mickey')\n              txn.commit()  # Changes are saved and a new transaction begins.\n\n              User.create(username='huey')\n              txn.rollback()  # \"huey\" will not be saved.\n\n              User.create(username='zaizee')\n\n          # Print the usernames of all users.\n          print([u.username for u in User.select()])\n\n          # Prints [\"mickey\", \"zaizee\"]\n\n      If an unhandled exception occurs in the block, the block is rolled-back\n      and the exception propagates.\n\n   .. method:: transaction(...)\n\n      Create a context-manager or decorator that runs all queries in the\n      wrapped block in a transaction.\n\n      Database-specific parameters:\n\n      :class:`PostgresqlDatabase` and :class:`MySQLDatabase` accept an\n      ``isolation_level`` parameter. :class:`SqliteDatabase` accepts a\n      ``lock_type`` parameter. Refer to :ref:`sqlite-locking` and :ref:`postgres-isolation`\n      for discussion.\n\n      :param str isolation_level: Isolation strategy: READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SERIALIZABLE\n      :param str lock_type: Locking strategy: DEFERRED, IMMEDIATE, EXCLUSIVE.\n\n      .. code-block:: python\n\n         with db.transaction() as txn:\n             User.create(username='mickey')\n             txn.commit()         # Commit now; a new transaction begins.\n             User.create(username='huey')\n             txn.rollback()       # Roll back huey; a new transaction begins.\n             User.create(username='zaizee')\n         # zaizee is committed when the block exits.\n\n      Transactions can be committed or rolled-back within the wrapped block.\n      If this occurs, a new transaction is begun.\n\n      .. warning::\n         If you attempt to nest transactions with peewee using the\n         :meth:`~Database.transaction` context manager, only the outer-most\n         transaction will be used.\n\n         As this may lead to unpredictable behavior, it is recommended that\n         you use :meth:`~Database.atomic`.\n\n   .. method:: savepoint()\n\n      Create a context-manager or decorator that runs all queries in the\n      wrapped block in a savepoint. Savepoints can be nested arbitrarily, but\n      must occur within a transaction.\n\n      .. code-block:: python\n\n         with db.transaction() as txn:\n             with db.savepoint() as sp:\n                 User.create(username='mickey')\n\n             with db.savepoint() as sp2:\n                 User.create(username='zaizee')\n                 sp2.rollback()  # \"zaizee\" is not saved.\n                 User.create(username='huey')\n\n         # mickey and huey were created.\n\n      Savepoints can be committed or rolled-back within the wrapped block.\n      If this occurs, a new savepoint is begun.\n\n   .. method:: manual_commit()\n\n      Create a context-manager or decorator which disables Peewee's transaction\n      management for the wrapped block.\n\n      Example:\n\n      .. code-block:: python\n\n         with db.manual_commit():\n             db.begin()  # Begin transaction explicitly.\n             try:\n                 user.delete_instance(recursive=True)\n             except:\n                 db.rollback()  # Rollback -- an error occurred.\n                 raise\n             else:\n                 try:\n                     db.commit()  # Attempt to commit changes.\n                 except:\n                     db.rollback()  # Error committing, rollback.\n                     raise\n\n      The above code is equivalent to the following:\n\n      .. code-block:: python\n\n         with db.atomic():\n             user.delete_instance(recursive=True)\n\n   .. method:: session_start()\n\n      Begin a new transaction (without using a context-manager or decorator).\n      This method is useful if you intend to execute a sequence of operations\n      inside a transaction, but using a decorator or context-manager would\n      not be appropriate.\n\n      It is strongly advised that you use the :meth:`Database.atomic`\n      method whenever possible for managing transactions/savepoints. The\n      ``atomic`` method correctly manages nesting, uses the appropriate\n      construction (e.g., transaction-vs-savepoint), and always cleans up\n      after itself.\n\n      The :meth:`~Database.session_start` method should only be used\n      if the sequence of operations does not easily lend itself to\n      wrapping using either a context-manager or decorator.\n\n      You must *always* call either :meth:`~Database.session_commit`\n      or :meth:`~Database.session_rollback` after calling the\n      ``session_start`` method.\n\n   .. method:: session_commit()\n\n      Commit any changes made during a transaction begun with\n      :meth:`~Database.session_start`.\n\n   .. method:: session_rollback()\n\n      Roll back any changes made during a transaction begun with\n      :meth:`~Database.session_start`.\n\n   .. method:: begin()\n\n      Begin a transaction when using manual-commit mode.\n\n      This method should only be used in conjunction with the\n      :meth:`~Database.manual_commit` context manager.\n\n   .. method:: commit()\n\n      Manually commit the currently-active transaction.\n\n      This method should only be used in conjunction with the\n      :meth:`~Database.manual_commit` context manager.\n\n   .. method:: rollback()\n\n      Manually roll-back the currently-active transaction.\n\n      This method should only be used in conjunction with the\n      :meth:`~Database.manual_commit` context manager.\n\n   .. method:: in_transaction()\n\n      :return: whether or not a transaction is currently open.\n      :rtype: bool\n\n      .. code-block:: python\n\n         with db.atomic() as tx:\n             assert db.in_transaction()\n\n         assert not db.in_transaction()  # No longer in transaction.\n\n   .. method:: batch_commit(it, n)\n\n      :param iterable it: an iterable whose items will be yielded.\n      :param int n: commit every *n* items.\n      :return: an equivalent iterable to the one provided, with the addition\n          that groups of *n* items will be yielded in a transaction.\n\n      Simplify batching large operations, such as inserts, updates, etc. Pass\n      in an iterable and the number of items-per-batch, and the items will be\n      returned by an equivalent iterator that wraps each batch in a\n      transaction.\n\n      Example:\n\n      .. code-block:: python\n\n         # Some list or iterable containing data to insert.\n         row_data = [{'username': 'u1'}, {'username': 'u2'}, ...]\n\n         # Insert all data, committing every 100 rows. If, for example,\n         # there are 789 items in the list, then there will be a total of\n         # 8 transactions (7x100 and 1x89).\n         for row in db.batch_commit(row_data, 100):\n             user = User.create(**row)\n\n             # Now let's suppose we need to do something w/the user.\n             user.call_method()\n\n      A more efficient option is to batch the data into a multi-value ``INSERT``\n      statement (for example, using :meth:`Model.insert_many`). Use this\n      approach instead wherever possible:\n\n      .. code-block:: python\n\n         with db.atomic():\n             for idx in range(0, len(row_data), 100):\n                 # Insert 100 rows at a time.\n                 rows = row_data[idx:idx + 100]\n                 User.insert_many(rows).execute()\n\n   .. method:: table_exists(table, schema=None)\n\n      :param str table: Table name.\n      :param str schema: Schema name (optional).\n      :return: ``bool`` indicating whether table exists.\n\n   .. method:: get_tables(schema=None)\n\n      :param str schema: Schema name (optional).\n      :return: a list of table names in the database.\n\n   .. method:: get_indexes(table, schema=None)\n\n      :param str table: Table name.\n      :param str schema: Schema name (optional).\n\n      Return a list of :class:`IndexMetadata` tuples.\n\n      Example:\n\n      .. code-block:: python\n\n         print(db.get_indexes('entry'))\n         [IndexMetadata(\n              name='entry_public_list',\n              sql='CREATE INDEX \"entry_public_list\" ...',\n              columns=['timestamp'],\n              unique=False,\n              table='entry'),\n          IndexMetadata(\n              name='entry_slug',\n              sql='CREATE UNIQUE INDEX \"entry_slug\" ON \"entry\" (\"slug\")',\n              columns=['slug'],\n              unique=True,\n              table='entry')]\n\n   .. method:: get_columns(table, schema=None)\n\n      :param str table: Table name.\n      :param str schema: Schema name (optional).\n\n      Return a list of :class:`ColumnMetadata` tuples.\n\n      Example:\n\n      .. code-block:: python\n\n         print(db.get_columns('entry'))\n         [ColumnMetadata(\n              name='id',\n              data_type='INTEGER',\n              null=False,\n              primary_key=True,\n              table='entry',\n              default=None),\n          ColumnMetadata(\n              name='title',\n              data_type='TEXT',\n              null=False,\n              primary_key=False,\n              table='entry',\n              default=None),\n          ...]\n\n   .. method:: get_primary_keys(table, schema=None)\n\n      :param str table: Table name.\n      :param str schema: Schema name (optional).\n\n      Return a list of column names that comprise the primary key.\n\n      Example:\n\n      .. code-block:: python\n\n         print(db.get_primary_keys('entry'))\n         ['id']\n\n   .. method:: get_foreign_keys(table, schema=None)\n\n      :param str table: Table name.\n      :param str schema: Schema name (optional).\n\n      Return a list of :class:`ForeignKeyMetadata` tuples for keys present\n      on the table.\n\n      Example:\n\n      .. code-block:: python\n\n         print(db.get_foreign_keys('entrytag'))\n         [ForeignKeyMetadata(\n              column='entry_id',\n              dest_table='entry',\n              dest_column='id',\n              table='entrytag'),\n          ...]\n\n   .. method:: get_views(schema=None)\n\n      :param str schema: Schema name (optional).\n\n      Return a list of :class:`ViewMetadata` tuples for VIEWs present in\n      the database.\n\n      Example:\n\n      .. code-block:: python\n\n         print(db.get_views())\n         [ViewMetadata(\n              name='entries_public',\n              sql='CREATE VIEW entries_public AS SELECT ... '),\n          ...]\n\n   .. method:: sequence_exists(seq)\n\n      :param str seq: Name of sequence.\n      :return: Whether sequence exists.\n      :rtype: bool\n\n      .. code-block:: python\n\n         if db.sequence_exists('user_id_seq'):\n             print('Sequence found.')\n\n   .. method:: create_tables(models, **options)\n\n      :param list models: A list of :class:`Model` classes.\n      :param options: Options to specify when calling\n          :meth:`Model.create_table`.\n\n      Create tables, indexes and associated constraints for the given list of\n      models.\n\n      Dependencies are resolved so that tables are created in the appropriate\n      order.\n\n   .. method:: drop_tables(models, **options)\n\n      :param list models: A list of :class:`Model` classes.\n      :param kwargs: Options to specify when calling\n          :meth:`Model.drop_table`.\n\n      Drop tables, indexes and constraints for the given list of models.\n\n      Dependencies are resolved so that tables are dropped in the appropriate\n      order.\n\n   .. method:: bind(models, bind_refs=True, bind_backrefs=True)\n\n      :param list models: One or more :class:`Model` classes to bind.\n      :param bool bind_refs: Bind related models.\n      :param bool bind_backrefs: Bind back-reference related models.\n\n      Bind the given list of models, and specified relations, to the\n      database.\n\n      .. code-block:: python\n\n         def setup_tests():\n             # Bind models to an in-memory SQLite database.\n             test_db = SqliteDatabase(':memory:')\n             test_db.bind([User, Tweet])\n\n   .. method:: bind_ctx(models, bind_refs=True, bind_backrefs=True)\n\n       :param list models: List of models to bind to the database.\n       :param bool bind_refs: Bind models that are referenced using\n           foreign-keys.\n       :param bool bind_backrefs: Bind models that reference the given model\n           with a foreign-key.\n\n       Create a context-manager that binds (associates) the given models with\n       the current database for the duration of the wrapped block.\n\n       Example:\n\n       .. code-block:: python\n\n           MODELS = [User, Tweet, Favorite]\n\n           # Bind the given models to the db for the duration of wrapped block.\n           def use_test_database(fn):\n               @wraps(fn)\n               def inner(self):\n                   with test_db.bind_ctx(MODELS):\n                       test_db.create_tables(MODELS)\n                       try:\n                           fn(self)\n                       finally:\n                           test_db.drop_tables(MODELS)\n               return inner\n\n\n           class TestSomething(TestCase):\n               @use_test_database\n               def test_something(self):\n                   # ... models are bound to test database ...\n                   pass\n\n   .. method:: extract_date(date_part, date_field)\n\n      :param str date_part: date part to extract, e.g. 'year'.\n      :param Node date_field: a SQL node containing a date/time, for example\n          a :class:`DateTimeField`.\n      :return: a SQL node representing a function call that will return the\n          provided date part.\n\n      Provides a compatible interface for extracting a portion of a datetime.\n\n      Example:\n\n      .. code-block:: python\n\n         query = (Tweet.select()\n                  .where(db.extract_date('year', Tweet.timestamp) == 2026))\n\n         # If Tweet.timestamp is a DateField or DateTimeField we could\n         # also write:\n         query = Tweet.select().where(Tweet.timestamp.year == 2026)\n\n   .. method:: truncate_date(date_part, date_field)\n\n      :param str date_part: date part to truncate to, e.g. 'day'.\n      :param Node date_field: a SQL node containing a date/time, for example\n          a :class:`DateTimeField`.\n      :return: a SQL node representing a function call that will return the\n          truncated date part.\n\n      Provides a compatible interface for truncating a datetime to the given\n      resolution.\n\n      Example:\n\n      .. code-block:: python\n\n         # Report on how many tweets made in each month.\n         query = (Tweet\n                  .select(\n                      db.truncate_date('month', Tweet.timestamp).alias('month'),\n                      fn.COUNT(Tweet.id).alias('count'))\n                  .group_by(db.truncate_date('month', Tweet.timestamp)))\n         for row in query:\n             print(row.month, '->', row.count)\n\n   .. method:: random()\n\n      :return: a SQL node representing a function call that returns a random\n          value.\n\n      A compatible interface for calling the appropriate random number\n      generation function provided by the database. For Postgres and Sqlite,\n      this is equivalent to ``fn.random()``, for MySQL ``fn.rand()``.\n\n\n.. class:: SqliteDatabase(database, pragmas=None, regexp_function=False, rank_functions=False, timeout=5, returning_clause=None,  **kwargs)\n\n   :param pragmas: Either a dictionary or a list of 2-tuples containing\n       pragma key and value to set every time a connection is opened.\n   :param bool regexp_function: Make the REGEXP function available.\n   :param bool rank_functions: Make the full-text search ranking functions\n      available (recommended only if using FTS4).\n   :param timeout: Set the busy-timeout on the SQLite driver (in seconds).\n   :param bool returning_clause: Use `RETURNING` clause automatically for bulk\n       INSERT queries (requires Sqlite 3.35 or newer).\n\n   Sqlite database implementation. :class:`SqliteDatabase` that provides\n   some advanced features only offered by Sqlite.\n\n   * Register user-defined functions, aggregates, window functions, collations\n   * Load extension modules distributed as shared libraries\n   * Advanced transactions (specify lock type)\n   * For additional features see :class:`CySqliteDatabase`.\n\n   Example of initializing a database and configuring some PRAGMAs:\n\n   .. code-block:: python\n\n      db = SqliteDatabase('my_app.db', pragmas=(\n          ('cache_size', -16000),  # 16MB\n          ('journal_mode', 'wal'),  # Use write-ahead-log journal mode.\n      ))\n\n      # Alternatively, pragmas can be specified using a dictionary.\n      db = SqliteDatabase('my_app.db', pragmas={'journal_mode': 'wal'})\n\n   .. method:: pragma(key, value=SENTINEL, permanent=False)\n\n      :param key: Setting name.\n      :param value: New value for the setting (optional).\n      :param permanent: Apply this pragma whenever a connection is opened.\n\n      Execute a PRAGMA query once on the active connection. If a value is not\n      specified, then the current value will be returned.\n\n      If ``permanent`` is specified, then the PRAGMA query will also be\n      executed whenever a new connection is opened, ensuring it is always\n      in-effect.\n\n   .. attribute:: cache_size\n\n      Get or set the cache_size pragma for the current connection.\n\n   .. attribute:: foreign_keys\n\n      Get or set the foreign_keys pragma for the current connection.\n\n   .. attribute:: journal_mode\n\n      Get or set the journal_mode pragma.\n\n   .. attribute:: journal_size_limit\n\n      Get or set the journal_size_limit pragma.\n\n   .. attribute:: mmap_size\n\n      Get or set the mmap_size pragma for the current connection.\n\n   .. attribute:: page_size\n\n      Get or set the page_size pragma.\n\n   .. attribute:: read_uncommitted\n\n      Get or set the read_uncommitted pragma for the current connection.\n\n   .. attribute:: synchronous\n\n      Get or set the synchronous pragma for the current connection.\n\n   .. attribute:: wal_autocheckpoint\n\n      Get or set the wal_autocheckpoint pragma for the current connection.\n\n   .. attribute:: timeout\n\n      Get or set the busy timeout (seconds).\n\n   .. method:: func(name=None, num_params=-1, deterministic=None)\n\n      :param str name: Name of the function (defaults to function name).\n      :param int num_params: Number of parameters the function accepts,\n          or -1 for any number.\n      :param bool deterministic: Whether the function is deterministic for a\n          given input (this is required to use the function in an index).\n          Requires Sqlite 3.20 or newer, and ``sqlite3`` driver support\n          (added to stdlib in Python 3.8).\n\n      Decorator to register a user-defined scalar function.\n\n      Example:\n\n      .. code-block:: python\n\n         @db.func('title_case')\n         def title_case(s):\n             return s.title() if s else ''\n\n         Book.select(fn.title_case(Book.title))\n\n   .. method:: register_function(fn, name=None, num_params=-1, deterministic=None)\n\n      :param fn: The user-defined scalar function.\n      :param str name: Name of function (defaults to function name)\n      :param int num_params: Number of arguments the function accepts, or\n          -1 for any number.\n      :param bool deterministic: Whether the function is deterministic for a\n          given input (this is required to use the function in an index).\n          Requires Sqlite 3.20 or newer, and ``sqlite3`` driver support\n          (added to stdlib in Python 3.8).\n\n      Register a user-defined scalar function. The function will be\n      registered each time a new connection is opened.  Additionally, if a\n      connection is already open, the function will be registered with the\n      open connection.\n\n   .. method:: aggregate(name=None, num_params=-1)\n\n      :param str name: Name of the aggregate (defaults to class name).\n      :param int num_params: Number of parameters the aggregate accepts,\n          or -1 for any number.\n\n      Class decorator to register a user-defined aggregate function.\n\n      Example:\n\n      .. code-block:: python\n\n         @db.aggregate('md5')\n         class MD5(object):\n             def initialize(self):\n                 self.md5 = hashlib.md5()\n\n             def step(self, value):\n                 self.md5.update(value)\n\n             def finalize(self):\n                 return self.md5.hexdigest()\n\n\n         @db.aggregate()\n         class Product(object):\n             '''Like SUM() except calculates cumulative product.'''\n             def __init__(self):\n                 self.product = 1\n\n             def step(self, value):\n                 self.product *= value\n\n             def finalize(self):\n                 return self.product\n\n   .. method:: register_aggregate(klass, name=None, num_params=-1)\n\n      :param klass: Class implementing aggregate API.\n      :param str name: Aggregate function name (defaults to name of class).\n      :param int num_params: Number of parameters the aggregate accepts, or\n          -1 for any number.\n\n      Register a user-defined aggregate function.\n\n      The function will be registered each time a new connection is opened.\n      Additionally, if a connection is already open, the aggregate will be\n      registered with the open connection.\n\n   .. method:: window_function(name=None, num_params=-1)\n\n      :param str name: Name of the window function (defaults to class name).\n      :param int num_params: Number of parameters the function accepts, or -1\n          for any number.\n\n      Class decorator to register a user-defined window function. Window\n      functions must define the following methods:\n\n      * ``step(<params>)`` - receive values from a row and update state.\n      * ``inverse(<params>)`` - inverse of ``step()`` for the given values.\n      * ``value()`` - return the current value of the window function.\n      * ``finalize()`` - return the final value of the window function.\n\n      Example:\n\n      .. code-block:: python\n\n         @db.window_function('my_sum')\n         class MySum(object):\n             def __init__(self):\n                 self._value = 0\n\n             def step(self, value):\n                 self._value += value\n\n             def inverse(self, value):\n                 self._value -= value\n\n             def value(self):\n                 return self._value\n\n             def finalize(self):\n                 return self._value\n\n   .. method:: register_window_function(klass, name=None, num_params=-1)\n\n      :param klass: Class implementing window function API.\n      :param str name: Window function name (defaults to name of class).\n      :param int num_params: Number of parameters the function accepts, or\n          -1 for any number.\n\n      Register a user-defined window function, requires SQLite >= 3.25.0.\n\n      The window function will be registered each time a new connection is\n      opened. Additionally, if a connection is already open, the window\n      function will be registered with the open connection.\n\n   .. method:: collation(name=None)\n\n      :param str name: Name of collation (defaults to function name)\n\n      Decorator to register a user-defined collation.\n\n      Example:\n\n      .. code-block:: python\n\n         @db.collation('reverse')\n         def collate_reverse(s1, s2):\n             return -cmp(s1, s2)\n\n         # Usage:\n         Book.select().order_by(collate_reverse.collation(Book.title))\n\n         # Equivalent:\n         Book.select().order_by(Book.title.asc(collation='reverse'))\n\n      As you might have noticed, the original ``collate_reverse`` function\n      has a special attribute called ``collation`` attached to it.  This\n      extra attribute provides a shorthand way to generate the SQL necessary\n      to use our custom collation.\n\n   .. method:: register_collation(fn, name=None)\n\n      :param fn: The collation function.\n      :param str name: Name of collation (defaults to function name)\n\n      Register a user-defined collation. The collation will be registered\n      each time a new connection is opened.  Additionally, if a connection is\n      already open, the collation will be registered with the open\n      connection.\n\n   .. method:: unregister_function(name)\n\n      :param name: Name of the user-defined scalar function.\n\n      Unregister the user-defined scalar function.\n\n   .. method:: unregister_aggregate(name)\n\n      :param name: Name of the user-defined aggregate.\n\n      Unregister the user-defined aggregate.\n\n   .. method:: unregister_window_function(name)\n\n      :param name: Name of the user-defined window function.\n\n      Unregister the user-defined window function.\n\n   .. method:: unregister_collation(name)\n\n      :param name: Name of the user-defined collation.\n\n      Unregister the user-defined collation.\n\n   .. method:: load_extension(extension_module)\n\n      Load the given extension shared library. Extension will be loaded for the\n      current connection as well as all subsequent connections.\n\n      .. code-block:: python\n\n         db = SqliteDatabase('my_app.db')\n\n         # Load extension in closure.so shared library.\n         db.load_extension('closure')\n\n   .. method:: unload_extension(extension_module)\n\n      Unregister extension from being automatically loaded on new connections.\n\n   .. method:: attach(filename, name)\n\n      :param str filename: Database to attach (or ``:memory:`` for in-memory)\n      :param str name: Schema name for attached database.\n      :return: boolean indicating success\n\n      Register another database file that will be attached to every database\n      connection. If the main database is currently connected, the new\n      database will be attached on the open connection.\n\n      Databases that are attached using this method will be attached\n      every time a database connection is opened.\n\n   .. method:: detach(name)\n\n      :param str name: Schema name for attached database.\n      :return: boolean indicating success\n\n      Unregister another database file that was attached previously with a\n      call to ``attach()``. If the main database is currently connected, the\n      attached database will be detached from the open connection.\n\n   .. method:: atomic(lock_type=None)\n\n      :param str lock_type: Locking strategy: DEFERRED, IMMEDIATE, EXCLUSIVE.\n\n      Create an atomic context-manager / decorator, optionally using the\n      specified locking strategy (default DEFERRED).\n\n      Lock type only applies to the outermost ``atomic()`` block.\n\n      .. seealso:: :ref:`sqlite-locking`\n\n   .. method:: transaction(lock_type=None)\n\n      :param str lock_type: Locking strategy: DEFERRED, IMMEDIATE, EXCLUSIVE.\n\n      Create a transaction context-manager / decorator, optionally using the\n      specified locking strategy (default DEFERRED).\n\n\n.. class:: PostgresqlDatabase(database, register_unicode=True, encoding=None, isolation_level=None, prefer_psycopg3=False)\n\n   Postgresql database implementation. Uses psycopg2 or psycopg3.\n\n   Additional optional keyword-parameters:\n\n   :param bool register_unicode: Register unicode types.\n   :param str encoding: Database encoding.\n   :param isolation_level: Isolation level constant, defined in the\n       ``psycopg2.extensions`` module or ``psycopg.IsolationLevel`` enum (psycopg3).\n       Also accepts string which is converted to the matching constant.\n   :type isolation_level: int, str\n   :param bool prefer_psycopg3: If both psycopg2 and psycopg3 are installed,\n       instruct Peewee to prefer psycopg3.\n\n   Example:\n\n   .. code-block:: python\n\n      db = PostgresqlDatabase(\n          'app',\n          user='postgres',\n          host='10.8.0.1',\n          port=5432,\n          password=os.environ['PGPASSWORD'],\n          isolation_level='SERIALIZABLE')\n\n   .. method:: interval(val)\n\n      :param str val: Time interval, e.g. ``'30 minutes'``\n      :return: expression representing the interval.\n\n   .. method:: set_time_zone(timezone)\n\n      :param str timezone: timezone name, e.g. \"US/Central\".\n      :return: no return value.\n\n      Set the timezone on the current connection. If no connection is open,\n      then one will be opened.\n\n   .. method:: set_isolation_level(isolation_level)\n\n      :param isolation_level: Isolation level constant, defined in the\n          ``psycopg2.extensions`` module or ``psycopg.IsolationLevel`` enum (psycopg3).\n          Also accepts string which is converted to the matching constant.\n          Set to ``None`` to use the server default.\n      :type isolation_level: int, str\n\n      Example of setting isolation level:\n\n      .. code-block:: python\n\n         # psycopg2 or psycopg3\n         db.set_isolation_level('SERIALIZABLE')\n\n         # psycopg2\n         from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE\n         db.set_isolation_level(ISOLATION_LEVEL_SERIALIZABLE)\n\n         # psycopg3\n         from psycopg import IsolationLevel\n         db.set_isolation_level(IsolationLevel.SERIALIZABLE)\n\n      Isolation level values in order of increasing strictness:\n\n      * READ UNCOMMITTED\n      * READ COMMITTED\n      * REPEATABLE READ\n      * SERIALIZABLE\n\n      See the `Postgresql transaction isolation docs <https://www.postgresql.org/docs/current/transaction-iso.html>`__\n      and Peewee's :ref:`postgres-isolation` for additional discussion.\n\n   .. method:: atomic(isolation_level=None)\n\n      :param isolation_level: Isolation strategy: SERIALIZABLE, READ COMMITTED, REPEATABLE READ, READ UNCOMMITTED\n      :type isolation_level: int, str\n\n      Create an atomic context-manager, optionally using the specified\n      isolation level (if unspecified, the connection default will be used).\n\n      Isolation level only applies to the outermost ``atomic()`` block.\n\n      See the `Postgresql transaction isolation docs <https://www.postgresql.org/docs/current/transaction-iso.html>`__\n      and Peewee's :ref:`postgres-isolation` for additional discussion.\n\n   .. method:: transaction(isolation_level=None)\n\n      :param isolation_level: Isolation strategy: SERIALIZABLE, READ COMMITTED, REPEATABLE READ, READ UNCOMMITTED\n      :type isolation_level: int, str\n\n      Create a transaction context-manager, optionally using the specified\n      isolation level (if unspecified, the connection default will be used).\n\n\n.. class:: MySQLDatabase(database, **kwargs)\n\n   MySQL database implementation.\n\n   Example:\n\n   .. code-block:: python\n\n      db = MySQLDatabase('app', host='10.8.0.1')\n\n   .. method:: atomic(isolation_level=None)\n\n      :param str isolation_level: Isolation strategy: SERIALIZABLE, READ COMMITTED, REPEATABLE READ, READ UNCOMMITTED\n\n      Create an atomic context-manager, optionally using the specified\n      isolation level (if unspecified, the server default will be used).\n\n      Isolation level only applies to the outermost ``atomic()`` block.\n\n   .. method:: transaction(isolation_level=None)\n\n      :param str isolation_level: Isolation strategy: SERIALIZABLE, READ COMMITTED, REPEATABLE READ, READ UNCOMMITTED\n\n      Create a transaction context-manager, optionally using the specified\n      isolation level (if unspecified, the server default will be used).\n\n.. _model-api:\n\nModel\n-----\n\n.. class:: Model(**kwargs)\n\n   :param kwargs: Mapping of field-name to value to initialize model with.\n\n   Model class provides a high-level abstraction for working with database\n   tables. Models are a one-to-one mapping with a database table (or a\n   table-like object, such as a view). Subclasses of ``Model`` declare any\n   number of :class:`Field` instances as class attributes. These fields\n   correspond to columns on the table.\n\n   Table-level operations, such as :meth:`~Model.select`,\n   :meth:`~Model.update`, :meth:`~Model.insert` and\n   :meth:`~Model.delete` are implemented as classmethods.\n\n   Row-level operations, such as :meth:`~Model.save` and :meth:`~Model.delete_instance`\n   are implemented as instancemethods.\n\n   Example:\n\n   .. code-block:: python\n\n      db = SqliteDatabase(':memory:')\n\n      class User(Model):\n          username = TextField()\n          join_date = DateTimeField(default=datetime.datetime.now)\n          is_admin = BooleanField(default=False)\n\n      admin = User(username='admin', is_admin=True)\n      admin.save()\n\n   .. classmethod:: alias([alias=None])\n\n      :param str alias: Optional name for alias.\n      :return: :class:`ModelAlias` instance.\n\n      Create an alias to the model-class. Model aliases allow you to\n      reference the same :class:`Model` multiple times in a query, for\n      example when doing a self-join or sub-query.\n\n      Example:\n\n      .. code-block:: python\n\n         Parent = Category.alias()\n         sq = (Category\n               .select(Category, Parent)\n               .join(Parent, on=(Category.parent == Parent.id))\n               .where(Parent.name == 'parent category'))\n\n   .. classmethod:: select(*fields)\n\n      :param fields: A list of model classes, field instances, functions or\n          expressions. If no arguments are provided, all columns for the\n          given model will be selected by default.\n      :return: :class:`ModelSelect` query.\n\n      Create a SELECT query. If no fields are explicitly provided, the query\n      will by default SELECT all the fields defined on the model, unless you\n      are using the query as a sub-query, in which case only the primary key\n      will be selected by default.\n\n      Example of selecting all columns:\n\n      .. code-block:: python\n\n         query = User.select().where(User.active == True).order_by(User.username)\n\n      Example of selecting all columns on *Tweet* and the parent model,\n      *User*. When the ``user`` foreign key is accessed on a *Tweet*\n      instance no additional query will be needed (see :ref:`N+1 <nplusone>`\n      for more details):\n\n      .. code-block:: python\n\n         query = (Tweet\n                  .select(Tweet, User)\n                  .join(User)\n                  .order_by(Tweet.created_date.desc()))\n\n         for tweet in query:\n             print(tweet.user.username, '->', tweet.content)\n\n      Example of subquery only selecting the primary key:\n\n      .. code-block:: python\n\n         inactive_users = User.select().where(User.active == False)\n\n         # Here, instead of defaulting to all columns, Peewee will default\n         # to only selecting the primary key.\n         Tweet.delete().where(Tweet.user.in_(inactive_users)).execute()\n\n      See :ref:`querying` for in-depth discussion.\n\n   .. classmethod:: update(__data=None, **update)\n\n      :param dict __data: ``dict`` of fields to values.\n      :param update: Field-name to value mapping.\n\n      Create an UPDATE query.\n\n      Example showing users being marked inactive if their registration has\n      expired:\n\n      .. code-block:: python\n\n         q = (User\n              .update(active=False)\n              .where(User.registration_expired == True))\n         q.execute()  # Execute the query, returning number of rows updated.\n\n      Example showing an atomic update:\n\n      .. code-block:: python\n\n         q = (PageView\n              .update({PageView.count: PageView.count + 1})\n              .where(PageView.url == url))\n         q.execute()  # Execute the query.\n\n      Update queries support :meth:`~WriteQuery.returning` with Postgresql and SQLite\n      to obtain the updated rows:\n\n      .. code-block:: python\n\n         query = (User\n                  .update(spam=True)\n                  .where(User.username.contains('billing'))\n                  .returning(User))\n         for user in query:\n             print(f'Marked {user.username} as spam')\n\n      See :ref:`updating-records` for additional discussion.\n\n      When an update query is executed, the number of rows modified will\n      be returned.\n\n   .. classmethod:: insert(__data=None, **insert)\n\n      :param dict __data: ``dict`` of fields to values to insert.\n      :param insert: Field-name to value mapping.\n\n      Create an INSERT query.\n\n      Insert a new row into the database. If any fields on the model have\n      default values, these values will be used if the fields are not\n      explicitly set in the ``insert`` dictionary.\n\n      Example showing creation of a new user:\n\n      .. code-block:: python\n\n         q = User.insert(username='admin', active=True, registration_expired=False)\n         q.execute()  # perform the insert.\n\n      You can also use :class:`Field` objects as the keys:\n\n      .. code-block:: python\n\n         new_id = User.insert({User.username: 'admin'}).execute()\n\n      If you have a model with a default value on one of the fields, and\n      that field is not specified in the ``insert`` parameter, the default\n      will be used:\n\n      .. code-block:: python\n\n         class User(Model):\n             username = CharField()\n             active = BooleanField(default=True)\n\n         # This INSERT query will automatically specify `active=True`:\n         User.insert(username='charlie')\n\n      Insert queries support :meth:`~WriteQuery.returning` with Postgresql and\n      SQLite to obtain the inserted rows:\n\n      .. code-block:: python\n\n         alice, = (User\n                   .insert(username='alice')\n                   .returning(User)\n                   .execute())\n         print(f'Added {alice.username} with id = {alice.id}')\n\n      When an insert query is executed on a table with an\n      auto-incrementing primary key, the primary key of the new row will\n      be returned.\n\n   .. classmethod:: insert_many(rows, fields=None)\n\n      :param rows: An iterable that yields rows to insert.\n      :param list fields: List of fields being inserted.\n      :return: number of rows modified (see note).\n\n      INSERT multiple rows of data.\n\n      The ``rows`` parameter must be an iterable that yields dictionaries or\n      tuples, where the ordering of the tuple values corresponds to the\n      fields specified in the ``fields`` argument. As with\n      :meth:`~Model.insert`, fields that are not specified in the\n      dictionary will use their default value, if one exists.\n\n      Due to the nature of bulk inserts, each row must contain the same\n      fields. The following will not work:\n\n      .. code-block:: python\n\n          Person.insert_many([\n              {'first_name': 'Peewee', 'last_name': 'Herman'},\n              {'first_name': 'Huey'},  # Missing \"last_name\"!\n          ]).execute()\n\n      Example of inserting multiple Users:\n\n      .. code-block:: python\n\n         data = [\n             ('charlie', True),\n             ('huey', False),\n             ('zaizee', False)]\n         query = User.insert_many(data, fields=[User.username, User.is_admin])\n         query.execute()\n\n      Equivalent example using dictionaries:\n\n      .. code-block:: python\n\n         data = [\n             {'username': 'charlie', 'is_admin': True},\n             {'username': 'huey', 'is_admin': False},\n             {'username': 'zaizee', 'is_admin': False}]\n\n         # Insert new rows.\n         User.insert_many(data).execute()\n\n      Because the ``rows`` parameter can be an arbitrary iterable, you can\n      also use a generator:\n\n      .. code-block:: python\n\n         def get_usernames():\n             for username in ['charlie', 'huey', 'peewee']:\n                 yield {'username': username}\n         User.insert_many(get_usernames()).execute()\n\n      Insert queries support :meth:`~WriteQuery.returning` with Postgresql and\n      SQLite to obtain the inserted rows:\n\n      .. code-block:: python\n\n         query = (User\n                  .insert_many([{'username': 'alice'}, {'username': 'bob'}])\n                  .returning(User))\n         for user in query:\n             print(f'Added {user.username} with id = {user.id}')\n\n      See :ref:`bulk-inserts` for additional discussion.\n\n      SQLite has a default limit of bound variables per statement.\n      Additional discussion: :ref:`bulk-inserts`.\n\n      SQLite documentation:\n\n      * `Max variable number limit <https://www.sqlite.org/limits.html#max_variable_number>`_\n      * `SQLite compile-time flags <https://www.sqlite.org/compile.html>`_\n\n      The default return value is the number of rows modified. However,\n      when using Postgresql, Peewee will return a cursor that yields the\n      primary-keys of the inserted rows. To disable this functionality with\n      Postgresql, append ``as_rowcount()`` to your insert.\n\n   .. classmethod:: insert_from(query, fields)\n\n      :param Select query: SELECT query to use as source of data.\n      :param fields: Fields to insert data into.\n      :return: number of rows modified (see note).\n\n      Generates an ``INSERT INTO ... SELECT`` query, copying rows from one\n      table into another without round-tripping data through Python:\n\n      .. code-block:: python\n\n         (TweetArchive\n          .insert_from(\n              Tweet.select(Tweet.user, Tweet.message),\n              fields=[TweetArchive.user, TweetArchive.message])\n          .execute())\n\n      See :ref:`bulk-inserts` for additional discussion.\n\n      The default return value is the number of rows modified. However,\n      when using Postgresql, Peewee will return a cursor that yields the\n      primary-keys of the inserted rows. To disable this functionality with\n      Postgresql, append ``as_rowcount()`` to your insert.\n\n   .. classmethod:: replace(__data=None, **insert)\n\n      :param dict __data: ``dict`` of fields to values to insert.\n      :param insert: Field-name to value mapping.\n\n      SQLite and MySQL support a ``REPLACE`` query, which will replace the row\n      in the event of a conflict:\n\n      .. code-block:: python\n\n         class User(BaseModel):\n             username = TextField(unique=True)\n             last_login = DateTimeField(null=True)\n\n         # Insert, or replace the entire existing row.\n         User.replace(username='huey', last_login=datetime.datetime.now()).execute()\n\n         # Equivalent using insert():\n         (User\n          .insert(username='huey', last_login=datetime.datetime.now())\n          .on_conflict_replace()\n          .execute())\n\n      See :ref:`upsert` for additional discussion.\n\n      ``replace`` deletes and re-inserts, which changes the primary key.\n      Use :meth:`Insert.on_conflict` when the primary key must be preserved,\n      or when only some columns should be updated.\n\n   .. classmethod:: replace_many(rows, fields=None)\n\n      :param rows: An iterable that yields rows to insert.\n      :param list fields: List of fields being inserted.\n\n      INSERT multiple rows of data using REPLACE for conflict-resolution.\n\n      .. seealso::\n         * :meth:`Model.insert_many` for syntax and examples.\n         * :ref:`upsert` for additional discussion.\n\n      ``replace_many`` may delete and re-insert rows, which changes the\n      primary key. Use :meth:`Insert.on_conflict` when the primary key must\n      be preserved, or when only some columns should be updated.\n\n   .. classmethod:: raw(sql, *params)\n\n      :param str sql: SQL query to execute.\n      :param params: Parameters for query.\n\n      Execute a SQL query directly.\n\n      Example selecting rows from the User table:\n\n      .. code-block:: python\n\n         q = User.raw('select id, username from users')\n         for user in q:\n             print(user.id, user.username)\n\n      Generally the use of ``raw`` is reserved for those cases where you\n      can significantly optimize a select query.\n\n   .. classmethod:: delete()\n\n      Create a DELETE query.\n\n      Example showing the deletion of all inactive users:\n\n      .. code-block:: python\n\n         q = User.delete().where(User.active == False)\n         q.execute()  # Remove the rows, return number of rows removed.\n\n      Delete queries support :meth:`~WriteQuery.returning` with Postgresql and\n      SQLite to obtain the deleted rows:\n\n      .. code-block:: python\n\n         query = (User\n                  .delete()\n                  .where(User.username.contains('billing'))\n                  .returning(User))\n         for user in query:\n             print(f'Deleted: {user.username}')\n\n      .. seealso::\n         * :ref:`deleting-records` for discussion and example usage.\n         * :meth:`Model.delete_instance` for deleting individual rows.\n\n   .. classmethod:: create(**query)\n\n      :param query: Mapping of field-name to value.\n\n      INSERT new row into table and return corresponding model instance.\n\n      Example showing the creation of a user (a row will be added to the\n      database):\n\n      .. code-block:: python\n\n         user = User.create(username='admin', password='test')\n\n      .. note::\n         ``create()`` is a shorthand for instantiate -> save.\n\n   .. classmethod:: bulk_create(model_list, batch_size=None)\n\n      :param iterable model_list: a list or other iterable of unsaved\n          :class:`Model` instances.\n      :param int batch_size: number of rows to batch per insert. If\n          unspecified, all models will be inserted in a single query.\n      :return: no return value.\n\n      Efficiently INSERT multiple unsaved model instances into the database.\n      Unlike :meth:`~Model.insert_many`, which accepts row data as a list\n      of either dictionaries or lists, this method accepts a list of unsaved\n      model instances.\n\n      Example:\n\n      .. code-block:: python\n\n         user_list = [User(username='u%s' % i) for i in range(10)]\n\n         with db.atomic():\n             # All 10 users are inserted in a single query.\n             User.bulk_create(user_list)\n\n      Batches:\n\n      .. code-block:: python\n\n          user_list = [User(username='u%s' % i) for i in range(10)]\n\n          with database.atomic():\n              # Will execute 4 INSERT queries (3 batches of 3, 1 batch of 1).\n              User.bulk_create(user_list, batch_size=3)\n\n      * The primary-key value for the newly-created models will only be\n        set if you are using Postgresql (which supports the ``RETURNING``\n        clause).\n      * SQLite has a limit of bound parameters for a query, typically 999\n        for Sqlite < 3.32.0, and 32766 for newer versions.\n      * **Strongly recommended** that you wrap the call in a transaction\n        using :meth:`Database.atomic`. Otherwise an error in a batch mid-way\n        through could leave the database in an inconsistent state.\n\n   .. classmethod:: bulk_update(model_list, fields, batch_size=None)\n\n      :param iterable model_list: a list or other iterable of\n          :class:`Model` instances.\n      :param list fields: list of fields to update.\n      :param int batch_size: number of rows to batch per insert. If\n          unspecified, all models will be inserted in a single query.\n      :return: total number of rows updated.\n\n      UPDATE multiple model instances in a single query by generating a\n      ``CASE`` statement mapping ids to new field values.\n\n      Example:\n\n      .. code-block:: python\n\n         # First, create 3 users.\n         u1, u2, u3 = [User.create(username='u%s' % i) for i in (1, 2, 3)]\n\n         # Now let's modify their usernames.\n         u1.username = 'u1-x'\n         u2.username = 'u2-y'\n         u3.username = 'u3-z'\n\n         # Update all three rows using a single UPDATE query.\n         User.bulk_update([u1, u2, u3], fields=[User.username])\n\n      This will result in executing the following SQL:\n\n      .. code-block:: sql\n\n         UPDATE \"users\" SET \"username\" = CASE \"users\".\"id\"\n             WHEN 1 THEN \"u1-x\"\n             WHEN 2 THEN \"u2-y\"\n             WHEN 3 THEN \"u3-z\" END\n         WHERE \"users\".\"id\" IN (1, 2, 3);\n\n      If you have a large number of objects to update, it is strongly\n      recommended that you specify a ``batch_size`` and wrap the operation in\n      a transaction:\n\n      .. code-block:: python\n\n         with database.atomic():\n             User.bulk_update(user_list, fields=['username'], batch_size=50)\n\n      ``bulk_update`` may be slower than a direct UPDATE query when the list is\n      very large, because the generated ``CASE`` expression grows proportionally.\n      For updates that can be expressed as a single WHERE clause, the direct\n      :meth:`~Model.update` approach is faster.\n\n   .. classmethod:: get(*query, **filters)\n\n      :param query: Zero or more :class:`Expression` objects.\n      :param filters: Mapping of field-name to value for Django-style filter.\n      :raises: :class:`DoesNotExist`\n      :return: Model instance matching the specified filters.\n\n      Retrieve a single model instance matching the given filters. If no\n      model is returned, a :class:`DoesNotExist` is raised.\n\n      .. code-block:: python\n\n         user = User.get(User.username == username, User.active == True)\n\n      This method is also exposed via the :class:`SelectQuery`, though it\n      takes no parameters:\n\n      .. code-block:: python\n\n         active = User.select().where(User.active == True)\n         try:\n             user = (active\n                     .where(\n                         (User.username == username) &\n                         (User.active == True))\n                     .get())\n         except User.DoesNotExist:\n             user = None\n\n      The :meth:`~Model.get` method is shorthand for selecting with a\n      limit of 1. It has the added behavior of raising an exception when\n      no matching row is found. If more than one row is found, the first\n      row returned by the database cursor will be used.\n\n   .. classmethod:: get_or_none(*query, **filters)\n\n      Identical to :meth:`Model.get` but returns ``None`` if no model\n      matches the given filters.\n\n      .. code-block:: python\n\n         active = User.select().where(User.active == True)\n         user = (active\n                 .where(\n                     (User.username == username) &\n                     (User.active == True))\n                 .get_or_none())\n\n   .. classmethod:: get_by_id(pk)\n\n      :param pk: Primary-key value.\n\n      Short-hand for calling :meth:`Model.get` specifying a lookup by\n      primary key. Raises a :class:`DoesNotExist` if instance with the\n      given primary key value does not exist.\n\n      Example:\n\n      .. code-block:: python\n\n         user = User.get_by_id(1)  # Returns user with id = 1.\n\n   .. classmethod:: set_by_id(key, value)\n\n      :param key: Primary-key value.\n      :param dict value: Mapping of field to value to update.\n\n      Short-hand for updating the data with the given primary-key. If no row\n      exists with the given primary key, no exception will be raised.\n\n      Example:\n\n      .. code-block:: python\n\n         # Set \"is_admin\" to True on user with id=3.\n         User.set_by_id(3, {'is_admin': True})\n\n   .. classmethod:: delete_by_id(pk)\n\n      :param pk: Primary-key value.\n\n      Short-hand for deleting the row with the given primary-key. If no row\n      exists with the given primary key, no exception will be raised.\n\n   .. classmethod:: get_or_create(**kwargs)\n\n      :param kwargs: Mapping of field-name to value.\n      :param defaults: Default values to use if creating a new row.\n      :return: Tuple of :class:`Model` instance and boolean indicating\n          if a new object was created.\n\n      Attempt to get the row matching the given filters. If no matching row\n      is found, create a new row.\n\n      .. warning:: Race-conditions are possible when using this method.\n\n      Example **without** ``get_or_create``:\n\n      .. code-block:: python\n\n         # Without `get_or_create`, we might write:\n         try:\n             person = Person.get(\n                 (Person.first_name == 'John') &\n                 (Person.last_name == 'Lennon'))\n         except Person.DoesNotExist:\n             person = Person.create(\n                 first_name='John',\n                 last_name='Lennon',\n                 birthday=datetime.date(1940, 10, 9))\n\n      Equivalent code using ``get_or_create``:\n\n      .. code-block:: python\n\n         person, created = Person.get_or_create(\n             first_name='John',\n             last_name='Lennon',\n             defaults={'birthday': datetime.date(1940, 10, 9)})\n\n   .. classmethod:: filter(*dq_nodes, **filters)\n\n      :param dq_nodes: Zero or more :class:`DQ` objects.\n      :param filters: Django-style filters.\n      :return: :class:`ModelSelect` query.\n\n   .. method:: get_id()\n\n      :return: The primary-key of the model instance.\n\n   .. method:: save(force_insert=False, only=None)\n\n      :param bool force_insert: Force INSERT query.\n      :param list only: Only save the given :class:`Field` instances.\n      :return: Number of rows modified.\n\n      Save the data in the model instance. By default, the presence of a\n      primary-key value will cause an UPDATE query to be executed.\n\n      Example showing saving a model instance:\n\n      .. code-block:: python\n\n         user = User()\n         user.username = 'some-user'  # does not touch the database\n         user.save()  # change is persisted to the db\n\n      When a model uses any primary key OTHER than an auto-incrementing integer\n      it is necessary to specify ``force_insert=True`` when calling ``save()``\n      with a new instance:\n\n      .. code-block:: python\n\n         class Tag(Model):\n             tag = TextField(primary_key=True)\n\n         t = Tag(tag='python')\n         t.save(force_insert=True)\n\n         # create() automatically specifies force_insert=True:\n         t = Tag.create(tag='sqlite')\n\n   .. attribute:: dirty_fields\n\n      Return list of fields that have been modified.\n\n      :rtype: list\n\n      If you just want to persist modified fields, you can call\n      ``model.save(only=model.dirty_fields)``.\n\n      To **always** save a model's dirty fields, use the Meta option\n      ``only_save_dirty = True``. Any calls to :meth:`Model.save()` will\n      only save the dirty fields by default:\n\n      .. code-block:: python\n\n         class Person(Model):\n             first_name = CharField()\n             last_name = CharField()\n             dob = DateField()\n\n             class Meta:\n                 database = db\n                 only_save_dirty = True\n\n      Peewee determines whether a field is \"dirty\" by observing when the\n      field attribute is set on a model instance. If the field contains a\n      value that is mutable, such as a dictionary instance, and that\n      dictionary is then modified, Peewee will not notice the change.\n\n      .. warning::\n         Do not do membership tests on this list, e.g. ``f in dirty_fields``\n         because if there is one or more fields in the dirty fields list,\n         the field equality override will return a truthy Expression object.\n         If you want to test if a field is dirty, instead\n         check ``f.name in model.dirty_field_names``.\n\n   .. attribute:: dirty_field_names\n\n      Return list of field names that have been modified.\n\n      :rtype: list\n\n   .. method:: is_dirty()\n\n      Return boolean indicating whether any fields were manually set.\n\n   .. method:: delete_instance(recursive=False, delete_nullable=False)\n\n       :param bool recursive: Delete related models.\n       :param bool delete_nullable: Delete related models that have a null\n           foreign key. If ``False`` nullable relations will be set to NULL.\n\n       Delete the given instance.  Any foreign keys set to cascade on\n       delete will be deleted automatically.  For more programmatic control,\n       you can specify ``recursive=True``, which will delete any non-nullable\n       related models (those that *are* nullable will be set to NULL).  If you\n       wish to delete all dependencies regardless of whether they are nullable,\n       set ``delete_nullable=True``.\n\n       Example:\n\n       .. code-block:: python\n\n          some_obj.delete_instance()\n\n       See :ref:`deleting-records` for additional discussion.\n\n   .. classmethod:: bind(database, bind_refs=True, bind_backrefs=True)\n\n      :param Database database: database to bind to.\n      :param bool bind_refs: Bind related models.\n      :param bool bind_backrefs: Bind back-reference related models.\n\n      Bind the model (and specified relations) to the given database.\n\n      See also: :meth:`Database.bind`.\n\n   .. classmethod:: bind_ctx(database, bind_refs=True, bind_backrefs=True)\n\n      Like :meth:`~Model.bind`, but returns a context manager that only\n      binds the models for the duration of the wrapped block.\n\n      See also: :meth:`Database.bind_ctx`.\n\n   .. classmethod:: table_exists()\n\n      :return: boolean indicating whether the table exists.\n\n   .. classmethod:: create_table(safe=True, **options)\n\n      :param bool safe: When ``True``, the create table query will\n          include an ``IF NOT EXISTS`` clause.\n\n      Create the model table, indexes, constraints and sequences.\n\n      Example:\n\n      .. code-block:: python\n\n         with database:\n             SomeModel.create_table()\n\n   .. classmethod:: drop_table(safe=True, **options)\n\n      :param bool safe: If set to ``True``, the drop table query will\n          include an ``IF EXISTS`` clause.\n\n      Drop the model table.\n\n   .. method:: truncate_table(restart_identity=False, cascade=False)\n\n      :param bool restart_identity: Restart the id sequence (postgresql-only).\n      :param bool cascade: Truncate related tables as well (postgresql-only).\n\n      Truncate (delete all rows) for the model.\n\n   .. classmethod:: index(*fields, unique=False, safe=True, where=None, using=None, name=None)\n\n      :param fields: Fields to index.\n      :param bool unique: Whether index is UNIQUE.\n      :param bool safe: Whether to add IF NOT EXISTS clause.\n      :param Expression where: Optional WHERE clause for index.\n      :param str using: Index algorithm.\n      :param str name: Optional index name.\n\n      Expressive method for declaring an index on a model. Wraps the\n      declaration of a :class:`ModelIndex` instance.\n\n      Examples:\n\n      .. code-block:: python\n\n         class Article(Model):\n             name = TextField()\n             timestamp = TimestampField()\n             status = IntegerField()\n             flags = BitField()\n\n             is_sticky = flags.flag(1)\n             is_favorite = flags.flag(2)\n\n         # CREATE INDEX ... ON \"article\" (\"name\", \"timestamp\" DESC)\n         idx = Article.index(Article.name, Article.timestamp.desc())\n\n         # Be sure to add the index to the model:\n         Article.add_index(idx)\n\n         # CREATE UNIQUE INDEX ... ON \"article\" (\"timestamp\" DESC, \"flags\" & 2)\n         # WHERE (\"status\" = 1)\n         idx = (Article\n                .index(Article.timestamp.desc(),\n                       Article.flags.bin_and(2),\n                       unique=True)\n                .where(Article.status == 1))\n\n         # Add index to model:\n         Article.add_index(idx)\n\n   .. classmethod:: add_index(*args, **kwargs)\n\n      :param args: a :class:`ModelIndex` instance, Field(s) to index,\n          or a :class:`SQL` instance that contains the SQL for creating\n          the index.\n      :param kwargs: Keyword arguments passed to :class:`ModelIndex`\n          constructor.\n\n      Add an index to the model's definition.\n\n      This method does not actually create the index in the database.\n      Rather, it adds the index definition to the model's metadata, so\n      that a subsequent call to :meth:`~Model.create_table` will\n      create the new index (along with the table).\n\n      Examples:\n\n      .. code-block:: python\n\n         class Article(Model):\n             name = TextField()\n             timestamp = TimestampField()\n             status = IntegerField()\n             flags = BitField()\n\n             is_sticky = flags.flag(1)\n             is_favorite = flags.flag(2)\n\n         # CREATE INDEX ... ON \"article\" (\"name\", \"timestamp\") WHERE \"status\" = 1\n         idx = Article.index(Article.name, Article.timestamp).where(Article.status == 1)\n         Article.add_index(idx)\n\n         # CREATE UNIQUE INDEX ... ON \"article\" (\"timestamp\" DESC, \"flags\" & 2)\n         ts_flags_idx = Article.index(\n             Article.timestamp.desc(),\n             Article.flags.bin_and(2),\n             unique=True)\n         Article.add_index(ts_flags_idx)\n\n         # You can also specify a list of fields and use the same keyword\n         # arguments that the ModelIndex constructor accepts:\n         Article.add_index(\n             Article.name,\n             Article.timestamp.desc(),\n             where=(Article.status == 1))\n\n         # Or even specify a SQL query directly:\n         Article.add_index(SQL('CREATE INDEX ...'))\n\n   .. method:: dependencies(search_nullable=False)\n\n      :param bool search_nullable: Search models related via a nullable\n          foreign key\n      :rtype: Generator expression yielding queries and foreign key fields.\n\n      Generate a list of queries of dependent models. Yields a 2-tuple\n      containing the query and corresponding foreign key field.  Useful for\n      searching dependencies of a model, i.e. things that would be orphaned\n      in the event of a delete.\n\n   .. method:: __iter__()\n\n      :return: a :class:`ModelSelect` for the given class.\n\n      Convenience function for iterating over all instances of a model.\n\n      Example:\n\n      .. code-block:: python\n\n         Setting.insert_many([\n             {'key': 'host', 'value': '192.168.1.2'},\n             {'key': 'port', 'value': '1337'},\n             {'key': 'user', 'value': 'nuggie'}]).execute()\n\n         # Load settings from db into dict.\n         settings = {setting.key: setting.value for setting in Setting}\n\n   .. method:: __len__()\n\n      :return: Count of rows in table.\n\n      Example:\n\n      .. code-block:: python\n\n         n_accounts = len(Account)\n\n         # Equivalent:\n         n_accounts = Account.select().count()\n\n\n.. class:: ModelAlias(model, alias=None)\n\n   :param Model model: Model class to reference.\n   :param str alias: (optional) name for alias.\n\n   Provide a separate reference to a model in a query.\n\n   With Peewee, we use :meth:`Model.alias` to alias a model class so it can be\n   referenced twice in a single query:\n\n   .. code-block:: python\n\n       Owner = User.alias()\n       query = (Favorite\n                .select(Favorite, Tweet.content, User.username, Owner.username)\n                .join_from(Favorite, Owner)  # Determine owner of favorite.\n                .join_from(Favorite, Tweet)  # Join favorite -> tweet.\n                .join_from(Tweet, User))     # Join tweet -> user.\n\n   See :ref:`relationships` for additional discussion.\n\n\n.. class:: Metadata(model, database=None, table_name=None, indexes=None, primary_key=None, constraints=None, schema=None, only_save_dirty=False, depends_on=None, options=None, without_rowid=False, strict_tables=False, **kwargs)\n\n   :param Model model: Model class.\n   :param Database database: database model is bound to.\n   :param str table_name: Specify table name for model.\n   :param list indexes: List of :class:`ModelIndex` objects.\n   :param primary_key: Primary key for model (only specified if this is a\n       :class:`CompositeKey` or ``False`` for no primary key.\n   :param list constraints: List of table constraints.\n   :param str schema: Schema table exists in.\n   :param bool only_save_dirty: When :meth:`~Model.save` is called, only\n       save the fields which have been modified.\n   :param dict options: Arbitrary options for the model.\n   :param bool without_rowid: Specify WITHOUT ROWID (sqlite only).\n   :param bool strict_tables: Specify STRICT (sqlite only, requires 3.37+).\n   :param kwargs: Arbitrary setting attributes and values.\n\n   Store metadata for a :class:`Model`.\n\n   This class should not be instantiated directly, but is instantiated using\n   the attributes of a :class:`Model` class' inner ``Meta`` class. Metadata\n   attributes are then available on ``Model._meta``.\n\n   Example:\n\n   .. code-block:: python\n\n      class User(Model):\n          ...\n          class Meta:\n              database = db\n              table_name = 'user'\n\n      # After class-creation, Meta configuration lives here:\n      isinstance(User._meta, Metadata)  # True\n      User._meta.database is db         # True\n      User._meta.table_name == 'user'   # True\n\n   .. seealso:: :ref:`model-options` for usage.\n\n   .. attribute:: table\n\n      Return a reference to the underlying :class:`Table` object.\n\n   .. method:: model_graph(refs=True, backrefs=True, depth_first=True)\n\n      :param bool refs: Follow foreign-key references.\n      :param bool backrefs: Follow foreign-key back-references.\n      :param bool depth_first: Do a depth-first search (``False`` for\n          breadth-first).\n\n      Traverse the model graph and return a list of 3-tuples, consisting of\n      ``(foreign key field, model class, is_backref)``.\n\n   .. method:: set_database(database)\n\n      :param Database database: database object to bind Model to.\n\n      Bind the model class to the given :class:`Database` instance.\n\n      .. warning::\n         This API should not need to be used. Instead, to change a\n         :class:`Model` database at run-time, use one of the following:\n\n         * :meth:`Model.bind`\n         * :meth:`Model.bind_ctx` (bind for scope of a context manager).\n         * :meth:`Database.bind`\n         * :meth:`Database.bind_ctx`\n\n   .. method:: set_table_name(table_name)\n\n      :param str table_name: table name to bind Model to.\n\n      Bind the model class to the given table name at run-time.\n\n\n.. class:: SubclassAwareMetadata\n\n   Metadata subclass that tracks :class:`Model` subclasses. Useful for\n   when you need to track all models in a project.\n\n   Example:\n\n   .. code-block:: python\n\n      from peewee import SubclassAwareMetadata\n\n      class Base(Model):\n          class Meta:\n              database = db\n              model_metadata_class = SubclassAwareMetadata\n\n      # Create 3 model classes that inherit from Base.\n      class A(Base): pass\n      class B(Base): pass\n      class C(Base): pass\n\n      # Now let's make a helper for changing the `schema` for each Model.\n      def change_schema(schema):\n          def _update(model):\n              model._meta.schema = schema\n          return _update\n\n      # Set all models to use \"schema1\", e.g. \"schema1.a\", \"schema1.b\", etc.\n      # Will apply the function to every subclass of Base.\n      Base._meta.map_models(change_schema('schema1'))\n\n      # Set all models to use \"schema2\", e.g. \"schema2.a\", \"schema2.b\", etc.\n      Base._meta.map_models(change_schema('schema2'))\n\n   .. method:: map_models(fn)\n\n      Apply a function to all subclasses.\n\n.. seealso::\n   :class:`~playhouse.shortcuts.ThreadSafeDatabaseMetadata` for a :class:`Metadata`\n   subclass that supports changing the ``database`` attribute at run-time in a\n   multi-threaded environment.\n\n\n.. class:: ModelSelect(model, fields_or_models)\n\n   :param Model model: Model class to select.\n   :param fields_or_models: List of fields or model classes to select.\n\n   Model-specific implementation of SELECT query.\n\n   .. method:: get()\n\n      :param Database database: database to execute query against.\n      :return: A single row from the database.\n      :raises: ``DoesNotExist`` if row not found.\n\n      Execute the query and return the first row, if it exists. Multiple\n      calls will result in multiple queries being executed.\n\n      If no matching row is found, raise ``DoesNotExist``.\n\n   .. method:: peek(n=1)\n\n      :param int n: Number of rows to return.\n      :return: A single row if n = 1, else a list of rows.\n\n      Execute the query and return the given number of rows from the start\n      of the cursor. This function may be called multiple times safely, and\n      will always return the first N rows of results.\n\n   .. method:: first(n=1)\n\n      :param int n: Number of rows to return.\n      :return: A single row if n = 1, else a list of rows.\n\n      Like the :meth:`~ModelSelect.peek` method, except a ``LIMIT`` is\n      applied to the query to ensure that only ``n`` rows are returned.\n      Multiple calls for the same value of ``n`` will not result in multiple\n      executions.\n\n      The query is altered in-place so it is **not** possible to call\n      :meth:`~ModelSelect.first` and then later iterate over the full\n      result-set using the same query object. Again, this is done to ensure\n      that multiple calls to ``first()`` will not result in multiple query\n      executions.\n\n   .. method:: scalar(as_tuple=False, as_dict=False)\n\n      :param bool as_tuple: Return the result as a tuple?\n      :param bool as_dict: Return the result as a dict?\n      :return: Single scalar value. If ``as_tuple = True``, a row tuple is\n          returned. If ``as_dict = True``, a row dict is returned.\n\n      Return a scalar value from the first row of results. If multiple\n      scalar values are anticipated (e.g. multiple aggregations in a single\n      query) then you may specify ``as_tuple=True`` to get the row tuple.\n\n      Example:\n\n      .. code-block:: python\n\n         query = Note.select(fn.MAX(Note.timestamp))\n         max_ts = query.scalar()\n\n         query = Note.select(fn.MAX(Note.timestamp), fn.COUNT(Note.id))\n         max_ts, n_notes = query.scalar(as_tuple=True)\n\n         query = Note.select(fn.COUNT(Note.id).alias('count'))\n         assert query.scalar(as_dict=True) == {'count': 123}\n\n   .. method:: count(clear_limit=False)\n\n      :param bool clear_limit: Clear any LIMIT clause when counting.\n      :return: Number of rows in the query result-set.\n\n      Return number of rows in the query result-set.\n\n      Implemented by running SELECT COUNT(1) FROM (<current query>).\n\n      Example:\n\n      .. code-block:: python\n\n         n = Tweet.select().where(Tweet.is_published == True).count()\n         print('%d published tweets' % n)\n\n   .. method:: exists()\n\n      :return: Whether any results exist for the current query.\n\n      Return a boolean indicating whether the current query has any results.\n\n      Example:\n\n      .. code-block:: python\n\n         if User.select().where(User.username == 'Alice').exists():\n             print('User found')\n\n   .. method:: dicts(as_dict=True)\n\n      :param bool as_dict: Specify whether to return rows as dictionaries.\n\n      Return rows as dictionaries.\n\n      Example:\n\n      .. code-block:: python\n         :emphasize-lines: 5, 8\n\n         query = (User\n                  .select(User.username, fn.COUNT(Tweet.id).alias('tweet_count'))\n                  .join(Tweet, JOIN.LEFT_OUTER)\n                  .group_by(User.username)\n                  .dicts())\n\n         for row in query:\n             print(row)  # {'username': 'Alice', 'tweet_count': 12}\n\n   .. method:: tuples(as_tuples=True)\n\n      :param bool as_tuples: Specify whether to return rows as tuples.\n\n      Return rows as tuples.\n\n      Example:\n\n      .. code-block:: python\n         :emphasize-lines: 5, 8\n\n         query = (User\n                  .select(User.username, fn.COUNT(Tweet.id).alias('tweet_count'))\n                  .join(Tweet, JOIN.LEFT_OUTER)\n                  .group_by(User.username)\n                  .tuples())\n\n         for row in query:\n             print(row)  # ('Alice', 12)\n\n   .. method:: namedtuples(as_namedtuple=True)\n\n      :param bool as_namedtuple: Specify whether to return rows as named\n          tuples.\n\n      Return rows as named tuples.\n\n      Example:\n\n      .. code-block:: python\n         :emphasize-lines: 5, 8\n\n         query = (User\n                  .select(User.username, fn.COUNT(Tweet.id).alias('tweet_count'))\n                  .join(Tweet, JOIN.LEFT_OUTER)\n                  .group_by(User.username)\n                  .namedtuples())\n\n         for row in query:\n             print(row)  # Row(username='Alice', tweet_count=12)\n\n   .. method:: objects(constructor=None)\n\n      :param constructor: Constructor (defaults to returning model instances)\n\n      Return result rows as objects created using the given constructor. The\n      default behavior is to create model instances.\n\n      This method can be used, when selecting field data from multiple\n      sources/models, to make all data available as attributes on the\n      model being queried (as opposed to constructing the graph of joined\n      model instances). For very complex queries this can have a positive\n      performance impact, especially iterating large result sets.\n\n      Example:\n\n      .. code-block:: python\n\n         query = (Tweet\n                  .select(Tweet.id, Tweet.content, User.username)\n                  .join(User)\n                  .objects())  # Apply all selections to Tweet instance.\n\n         # NOTE: the username is applied directly to the tweet object\n         # and accessed via `tweet.username`:\n         for tweet in query:\n             print(tweet.id, tweet.content, tweet.username)\n\n      Similarly, you can use :meth:`~ModelSelect.dicts`,\n      :meth:`~ModelSelect.tuples` or :meth:`~ModelSelect.namedtuples`\n      to achieve even more performance.\n\n   .. method:: models()\n\n      Return result rows as :class:`Model` instances, rebuilding the model\n      graph from explicitly selected and joined data. **This is the default**.\n\n      .. code-block:: python\n\n         query = (Tweet\n                  .select(Tweet, User)\n                  .join(User))\n\n         # Note that `tweet.user` is populated already since we SELECTed\n         # columns from the joined User model.\n         for tweet in query:\n             print(tweet.user.username, '->', tweet.content)\n\n      For an in-depth discussion of foreign-keys, joins and relationships\n      between models, refer to :ref:`relationships`.\n\n   .. method:: join(dest, join_type='INNER', on=None, src=None, attr=None)\n\n      :param dest: A :class:`Model`, :class:`ModelAlias`,\n          :class:`Select` query, or other object to join to.\n      :param str join_type: Join type, defaults to INNER.\n      :param on: Join predicate or a :class:`ForeignKeyField` to join on.\n      :param src: Explicitly specify the source of the join. If not specified\n          then the current *join context* will be used.\n      :param str attr: Attribute to use when projecting columns from the\n          joined model.\n\n      Join with another table-like object.\n\n      Join type may be one of:\n\n      * ``JOIN.INNER``\n      * ``JOIN.LEFT_OUTER``\n      * ``JOIN.RIGHT_OUTER``\n      * ``JOIN.FULL``\n      * ``JOIN.FULL_OUTER``\n      * ``JOIN.CROSS``\n\n      Example selecting tweets and joining on user in order to restrict to\n      only those tweets made by \"admin\" users:\n\n      .. code-block:: python\n\n         sq = Tweet.select().join(User).where(User.is_admin == True)\n\n      Example selecting users and joining on a particular foreign key field.\n      See the :ref:`example app <example>` for a real-life usage:\n\n      .. code-block:: python\n\n         sq = User.select().join(Relationship, on=Relationship.to_user)\n\n      For an in-depth discussion of foreign-keys, joins and relationships\n      between models, refer to :ref:`relationships`.\n\n   .. method:: join_from(src, dest, join_type='INNER', on=None, attr=None)\n\n      :param src: Source for join.\n      :param dest: Table to join to.\n\n      Use same parameter order as the non-model-specific\n      :meth:`~ModelSelect.join`. Bypasses the *join context* by requiring\n      the join source to be specified.\n\n   .. method:: switch(ctx=None)\n\n      :param ctx: A :class:`Model`, :class:`ModelAlias`, subquery, or\n          other object that was joined-on.\n\n      Switch the *join context* - the source which subsequent calls to\n      :meth:`~ModelSelect.join` will be joined against. Used for\n      specifying multiple joins against a single table.\n\n      See :ref:`relationships` for additional discussion.\n\n      If the ``ctx`` is not given, then the query's model will be used.\n\n      The following example selects from tweet and joins on both user and\n      tweet-flag:\n\n      .. code-block:: python\n\n          sq = Tweet.select().join(User).switch(Tweet).join(TweetFlag)\n\n          # Equivalent (since Tweet is the query's model)\n          sq = Tweet.select().join(User).switch().join(TweetFlag)\n\n   .. method:: filter(*args, **kwargs)\n\n      :param args: Zero or more :class:`DQ` objects.\n      :param kwargs: Django-style keyword-argument filters.\n\n      Use Django-style filters to express a WHERE clause. Joins can be\n      followed by chaining foreign-key fields. The supported operations are:\n\n      * ``eq`` - equals\n      * ``ne`` - not equals\n      * ``lt``, ``lte`` - less-than, less-than or equal-to\n      * ``gt``, ``gte`` - greater-than, greater-than or equal-to\n      * ``in`` - IN set of values\n      * ``is`` - IS (e.g. IS NULL).\n      * ``like``, ``ilike`` - LIKE and ILIKE (case-insensitive)\n      * ``regexp`` - regular expression match\n\n      Examples:\n\n      .. code-block:: python\n\n         # Get all tweets by user with username=\"peewee\".\n         q = Tweet.filter(user__username='peewee')\n\n         # Get all posts that are draft or published, and written after 2023.\n         q = Post.filter(\n             (DQ(status='draft') | DQ(status='published')),\n             timestamp__gte=datetime.date(2023, 1, 1))\n\n   .. method:: prefetch(*subqueries, prefetch_type=PREFETCH_TYPE.WHERE)\n\n      :param subqueries: A list of :class:`Model` classes or select\n          queries to prefetch.\n      :param prefetch_type: Query type to use for the subqueries.\n      :return: a list of models with selected relations prefetched.\n\n      Execute the query, prefetching the given additional resources.\n\n      Prefetch type may be one of:\n\n      * ``PREFETCH_TYPE.WHERE``\n      * ``PREFETCH_TYPE.JOIN``\n\n      See also :func:`prefetch` standalone function.\n\n      Example:\n\n      .. code-block:: python\n\n         # Fetch all Users and prefetch their associated tweets.\n         query = User.select().prefetch(Tweet)\n         for user in query:\n             print(user.username)\n             for tweet in user.tweets:\n                 print('  *', tweet.content)\n\n      Because ``prefetch`` must reconstruct a graph of models, it is\n      necessary to be sure that the foreign-key/primary-key of any\n      related models are selected, so that the related objects can be\n      mapped correctly.\n\n\n.. class:: DoesNotExist\n\n   Base exception class raised when a call to :meth:`Model.get` (or other\n   ``.get()`` method) fails to return a matching result. Model classes have a\n   model-specific subclass as a top-level attribute:\n\n   .. code-block:: python\n\n      def get_user(email):\n          try:\n              return User.get(fn.LOWER(User.email) == email.lower())\n          except User.DoesNotExist:\n              return None\n\n\n.. class:: PeeweeException\n\n   Base exception class for wrapped DB-API exceptions.\n\n.. class:: ImproperlyConfigured\n\n   Exception raised for configuration issues like missing drivers.\n\n.. class:: DatabaseError\n           DataError\n           IntegrityError\n           InterfaceError\n           InternalError\n           NotSupportedError\n           OperationalError\n           ProgrammingError\n\n   Exception wrappers for DB-API errors raised by the driver. Allows users to\n   catch the Peewee exception wrappers rather than the driver-specific types.\n\n\n.. _fields-api:\n\nFields\n------\n\n.. class:: Field(null=False, index=False, unique=False, column_name=None, default=None, primary_key=False, constraints=None, sequence=None, collation=None, unindexed=False, choices=None, help_text=None, verbose_name=None, index_type=None)\n\n   :param bool null: Field allows NULLs.\n   :param bool index: Create an index on field.\n   :param bool unique: Create a unique index on field.\n   :param str column_name: Specify column name for field.\n   :param default: Default value (enforced in Python, not on server).\n   :param bool primary_key: Field is the primary key.\n   :param list constraints: List of constraints to apply to column, for\n       example: ``[Check('price > 0')]``.\n   :param str sequence: Sequence name for field.\n   :param str collation: Collation name for field.\n   :param bool unindexed: Declare field UNINDEXED (sqlite only).\n   :param list choices: An iterable of 2-tuples mapping column values to\n       display labels. Used for metadata purposes only, to help when\n       displaying a dropdown of choices for field values, for example.\n   :param str help_text: Help-text for field, metadata purposes only.\n   :param str verbose_name: Verbose name for field, metadata purposes only.\n   :param str index_type: Specify index type (postgres only), e.g. 'BRIN'.\n\n   Fields on a :class:`Model` are analogous to columns on a table.\n\n   .. attribute:: field_type = '<some field type>'\n\n      Attribute used to map this field to a column type, e.g. \"INT\". See\n      the ``FIELD`` object in the source for more information.\n\n   .. attribute:: column\n\n      Retrieve a reference to the underlying :class:`Column` object.\n\n   .. attribute:: model\n\n      The model the field is bound to.\n\n   .. attribute:: name\n\n      The name of the field.\n\n   .. method:: db_value(value)\n\n      Coerce a Python value into a value suitable for storage in the\n      database. Sub-classes operating on special data-types will most likely\n      want to override this method.\n\n      Example:\n\n      .. code-block:: python\n         :emphasize-lines: 3\n\n         class PickleField(BlobField):\n             # Values going from Python -> Database should be pickled.\n             def db_value(self, value):\n                 if value is not None:\n                     return pickle.dumps(value, pickle.HIGHEST_PROTOCOL)\n\n             # Values coming from the Database -> Python should be unpickled.\n             def python_value(self, value):\n                 if value is not None:\n                     return pickle.loads(value)\n\n   .. method:: python_value(value)\n\n      Coerce a value from the database into a Python object. Sub-classes\n      operating on special data-types will most likely want to override this\n      method.\n\n      Example:\n\n      .. code-block:: python\n         :emphasize-lines: 8\n\n         class PickleField(BlobField):\n             # Values going from Python -> Database should be pickled.\n             def db_value(self, value):\n                 if value is not None:\n                     return pickle.dumps(value, pickle.HIGHEST_PROTOCOL)\n\n             # Values coming from the Database -> Python should be unpickled.\n             def python_value(self, value):\n                 if value is not None:\n                     return pickle.loads(value)\n\n   .. method:: coerce(value)\n\n      This method is a shorthand that is used, by default, by both\n      :meth:`~Field.db_value` and :meth:`~Field.python_value`.\n\n      :param value: arbitrary data from app or backend\n      :rtype: python data type\n\n.. class:: IntegerField\n\n   Field class for storing integers.\n\n.. class:: BigIntegerField\n\n   Field class for storing big integers (if supported by database).\n\n.. class:: SmallIntegerField\n\n   Field class for storing small integers (if supported by database).\n\n.. class:: AutoField\n\n   Field class for storing auto-incrementing primary keys.\n\n   In SQLite, for performance reasons, the default primary key type simply\n   uses the max existing value + 1 for new values, as opposed to the max\n   ever value + 1. This means deleted records can have their primary keys\n   reused. In conjunction with SQLite having foreign keys disabled by\n   default (meaning ON DELETE is ignored, even if you specify it\n   explicitly), this can lead to surprising and dangerous behaviour. To\n   avoid this, you may want to use one or both of\n   :class:`AutoIncrementField` and ``pragmas=[('foreign_keys', 'on')]``\n   when you instantiate :class:`SqliteDatabase`.\n\n.. class:: BigAutoField\n\n   Field class for storing auto-incrementing primary keys using 64-bits.\n\n.. class:: IdentityField(generate_always=False)\n\n   :param bool generate_always: if specified, then the identity will always be\n       generated (and specifying the value explicitly during INSERT will raise\n       a programming error). Otherwise, the identity value is only generated\n       as-needed.\n\n   Field class for storing auto-incrementing primary keys using the\n   Postgresql *IDENTITY* column type. The column definition ends up looking\n   like this:\n\n   .. code-block:: python\n\n      id = IdentityField()\n      # \"id\" INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY\n\n   .. attention:: Requires Postgresql >= 10\n\n.. class:: FloatField\n\n   Field class for storing floating-point numbers.\n\n.. class:: DoubleField\n\n   Field class for storing double-precision floating-point numbers.\n\n.. class:: DecimalField(max_digits=10, decimal_places=5, auto_round=False, rounding=None, **kwargs)\n\n   :param int max_digits: Maximum digits to store.\n   :param int decimal_places: Maximum precision.\n   :param bool auto_round: Automatically round values.\n   :param rounding: Defaults to ``decimal.DefaultContext.rounding``.\n\n    Field class for storing decimal numbers. Values are represented as\n    ``decimal.Decimal`` objects.\n\n.. class:: CharField(max_length=255)\n\n   Field class for storing strings.\n\n   Values that exceed length are NOT truncated automatically.\n\n.. class:: FixedCharField\n\n   Field class for storing fixed-length strings.\n\n   Values that exceed length are not truncated automatically.\n\n.. class:: TextField\n\n   Field class for storing text.\n\n.. class:: BlobField\n\n   Field class for storing binary data.\n\n.. class:: BitField\n\n   Field class for storing options in a 64-bit integer column.\n\n   Usage:\n\n   .. code-block:: python\n\n      class Post(Model):\n          content = TextField()\n          flags = BitField()\n\n          is_favorite = flags.flag(1)\n          is_sticky = flags.flag(2)\n          is_minimized = flags.flag(4)\n          is_deleted = flags.flag(8)\n\n      >>> p = Post()\n      >>> p.is_sticky = True\n      >>> p.is_minimized = True\n      >>> print(p.flags)  # Prints 4 | 2 --> \"6\"\n      6\n      >>> p.is_favorite\n      False\n      >>> p.is_sticky\n      True\n\n   We can use the flags on the Post class to build expressions in queries as\n   well:\n\n   .. code-block:: python\n\n      # Generates a WHERE clause that looks like:\n      # WHERE (post.flags & 1 != 0)\n      query = Post.select().where(Post.is_favorite)\n\n      # Query for sticky + favorite posts:\n      query = Post.select().where(Post.is_sticky & Post.is_favorite)\n\n   When bulk-updating one or more bits in a :class:`BitField`, you can use\n   bitwise operators to set or clear one or more bits:\n\n   .. code-block:: python\n\n      # Set the 4th bit on all Post objects.\n      Post.update(flags=Post.flags | 8).execute()\n\n      # Clear the 1st and 3rd bits on all Post objects.\n      Post.update(flags=Post.flags & ~(1 | 4)).execute()\n\n   For simple operations, the flags provide handy ``set()`` and ``clear()``\n   methods for setting or clearing an individual bit:\n\n   .. code-block:: python\n\n      # Set the \"is_deleted\" bit on all posts.\n      Post.update(flags=Post.is_deleted.set()).execute()\n\n      # Clear the \"is_deleted\" bit on all posts.\n      Post.update(flags=Post.is_deleted.clear()).execute()\n\n   .. method:: flag(value=None)\n\n      :param int value: Value associated with flag, typically a power of 2.\n\n      Returns a descriptor that can get or set specific bits in the overall\n      value. When accessed on the class itself, it returns a\n      :class:`Expression` object suitable for use in a query.\n\n      If the value is not provided, it is assumed that each flag will be an\n      increasing power of 2, so if you had four flags, they would have the\n      values 1, 2, 4, 8.\n\n.. class:: BigBitField\n\n   Field class for storing arbitrarily-large bitmaps in a ``BLOB``. The field\n   will grow the underlying buffer as necessary, ensuring there are enough\n   bytes of data to support the number of bits of data being stored.\n\n   Example usage:\n\n   .. code-block:: python\n\n      class Bitmap(Model):\n          data = BigBitField()\n\n      bitmap = Bitmap()\n\n      # Sets the ith bit, e.g. the 1st bit, the 11th bit, the 63rd, etc.\n      bits_to_set = (1, 11, 63, 31, 55, 48, 100, 99)\n      for bit_idx in bits_to_set:\n          bitmap.data.set_bit(bit_idx)\n\n      # We can test whether a bit is set using \"is_set\":\n      assert bitmap.data.is_set(11)\n      assert not bitmap.data.is_set(12)\n\n      # We can clear a bit:\n      bitmap.data.clear_bit(11)\n      assert not bitmap.data.is_set(11)\n\n      # We can also \"toggle\" a bit. Recall that the 63rd bit was set earlier.\n      assert bitmap.data.toggle_bit(63) is False\n      assert bitmap.data.toggle_bit(63) is True\n      assert bitmap.data.is_set(63)\n\n      # BigBitField supports item accessor by bit-number, e.g.:\n      assert bitmap.data[63]\n      bitmap.data[0] = 1\n      del bitmap.data[0]\n\n      # We can also combine bitmaps using bitwise operators, e.g.\n      b = Bitmap(data=b'\\x01')\n      b.data |= b'\\x02'\n      assert list(b.data) == [1, 1, 0, 0, 0, 0, 0, 0]\n      assert len(b.data) == 1\n\n   .. method:: clear()\n\n      Clears the bitmap and sets length to 0.\n\n   .. method:: set_bit(idx)\n\n      :param int idx: Bit to set, indexed starting from zero.\n\n      Sets the *idx*-th bit in the bitmap.\n\n   .. method:: clear_bit(idx)\n\n      :param int idx: Bit to clear, indexed starting from zero.\n\n      Clears the *idx*-th bit in the bitmap.\n\n   .. method:: toggle_bit(idx)\n\n      :param int idx: Bit to toggle, indexed starting from zero.\n      :return: Whether the bit is set or not.\n\n      Toggles the *idx*-th bit in the bitmap and returns whether the bit is\n      set or not.\n\n      Example:\n\n      .. code-block:: pycon\n\n          >>> bitmap = Bitmap()\n          >>> bitmap.data.toggle_bit(10)  # Toggle the 10th bit.\n          True\n          >>> bitmap.data.toggle_bit(10)  # This will clear the 10th bit.\n          False\n\n   .. method:: is_set(idx)\n\n      :param int idx: Bit index, indexed starting from zero.\n      :return: Whether the bit is set or not.\n\n      Returns boolean indicating whether the *idx*-th bit is set or not.\n\n   .. method:: __getitem__(idx)\n\n      Same as :meth:`~BigBitField.is_set`\n\n   .. method:: __setitem__(idx, value)\n\n      Set the bit at ``idx`` to value (True or False).\n\n   .. method:: __delitem__(idx)\n\n      Same as :meth:`~BigBitField.clear_bit`\n\n   .. method:: __len__()\n\n      Return the length of the bitmap **in bytes**.\n\n   .. method:: __iter__()\n\n      Returns an iterator yielding 1 or 0 for each bit in the bitmap.\n\n   .. method:: __and__(other)\n\n      :param other: Either :class:`BigBitField`, ``bytes``, ``bytearray``\n          or ``memoryview`` object.\n      :return: bitwise ``and`` of two bitmaps.\n\n   .. method:: __or__(other)\n\n      :param other: Either :class:`BigBitField`, ``bytes``, ``bytearray``\n          or ``memoryview`` object.\n      :return: bitwise ``or`` of two bitmaps.\n\n   .. method:: __xor__(other)\n\n      :param other: Either :class:`BigBitField`, ``bytes``, ``bytearray``\n          or ``memoryview`` object.\n      :return: bitwise ``xor`` of two bitmaps.\n\n\n.. class:: UUIDField\n\n   Field class for storing ``uuid.UUID`` objects. With Postgres, the\n   underlying column's data-type will be *UUID*. Since SQLite and MySQL do not\n   have a native UUID type, the UUID is stored as a *VARCHAR* instead.\n\n.. class:: BinaryUUIDField\n\n   Field class for storing ``uuid.UUID`` objects efficiently in 16-bytes. Uses\n   the database's *BLOB* data-type (or *VARBINARY* in MySQL, or *BYTEA* in\n   Postgres).\n\n.. class:: DateTimeField(formats=None, **kwargs)\n\n   :param list formats: A list of format strings to use when coercing a string\n       to a date-time.\n\n   Field class for storing ``datetime.datetime`` objects.\n\n   Accepts a special parameter ``formats``, which contains a list of formats\n   the datetime can be encoded with (for databases that do not have support\n   for a native datetime data-type). The default supported formats are:\n\n   .. code-block:: python\n\n      '%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond\n      '%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second\n      '%Y-%m-%d' # year-month-day\n\n   SQLite does not have a native datetime data-type, so datetimes are\n   stored as strings. This is handled transparently by Peewee, but if you\n   have pre-existing data you should ensure it is stored as\n   ``YYYY-mm-dd HH:MM:SS`` or one of the other supported formats.\n\n   .. attribute:: year\n\n      Reference the year of the value stored in the column in a query.\n\n      .. code-block:: python\n\n         Blog.select().where(Blog.pub_date.year == 2018)\n\n   .. attribute:: month\n\n      Reference the month of the value stored in the column in a query.\n\n   .. attribute:: day\n\n      Reference the day of the value stored in the column in a query.\n\n   .. attribute:: hour\n\n      Reference the hour of the value stored in the column in a query.\n\n   .. attribute:: minute\n\n      Reference the minute of the value stored in the column in a query.\n\n   .. attribute:: second\n\n      Reference the second of the value stored in the column in a query.\n\n   .. method:: to_timestamp()\n\n      Method that returns a database-specific function call that will allow\n      you to work with the given date-time value as a numeric timestamp. This\n      can sometimes simplify tasks like date math in a compatible way.\n\n      Example:\n\n      .. code-block:: python\n\n          # Find all events that are exactly 1 hour long.\n          query = (Event\n                   .select()\n                   .where((Event.start.to_timestamp() + 3600) ==\n                          Event.stop.to_timestamp())\n                   .order_by(Event.start))\n\n   .. method:: truncate(date_part)\n\n      :param str date_part: year, month, day, hour, minute or second.\n      :return: expression node to truncate date/time to given resolution.\n\n      Truncates the value in the column to the given part. This method is\n      useful for finding all rows within a given month, for instance.\n\n\n.. class:: DateField(formats=None, **kwargs)\n\n   :param list formats: A list of format strings to use when coercing a string\n       to a date.\n\n   Field class for storing ``datetime.date`` objects.\n\n   Accepts a special parameter ``formats``, which contains a list of formats\n   the datetime can be encoded with (for databases that do not have support\n   for a native date data-type). The default supported formats are:\n\n   .. code-block:: python\n\n      '%Y-%m-%d' # year-month-day\n      '%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second\n      '%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond\n\n   .. note::\n      If the incoming value does not match a format, it is returned as-is.\n\n   .. attribute:: year\n\n      Reference the year of the value stored in the column in a query.\n\n      .. code-block:: python\n\n         Person.select().where(Person.dob.year == 1983)\n\n   .. attribute:: month\n\n      Reference the month of the value stored in the column in a query.\n\n   .. attribute:: day\n\n      Reference the day of the value stored in the column in a query.\n\n   .. method:: to_timestamp()\n\n      See :meth:`DateTimeField.to_timestamp`.\n\n   .. method:: truncate(date_part)\n\n      See :meth:`DateTimeField.truncate`. Note that only *year*, *month*,\n      and *day* are meaningful for :class:`DateField`.\n\n\n.. class:: TimeField(formats=None, **kwargs)\n\n   :param list formats: A list of format strings to use when coercing a string\n       to a time.\n\n   Field class for storing ``datetime.time`` objects (not ``timedelta``).\n\n   Accepts a special parameter ``formats``, which contains a list of formats\n   the datetime can be encoded with (for databases that do not have support\n   for a native time data-type). The default supported formats are:\n\n   .. code-block:: python\n\n      '%H:%M:%S.%f' # hour:minute:second.microsecond\n      '%H:%M:%S' # hour:minute:second\n      '%H:%M' # hour:minute\n      '%Y-%m-%d %H:%M:%S.%f' # year-month-day hour-minute-second.microsecond\n      '%Y-%m-%d %H:%M:%S' # year-month-day hour-minute-second\n\n   .. note::\n      If the incoming value does not match a format, it is returned as-is.\n\n   .. attribute:: hour\n\n      Reference the hour of the value stored in the column in a query.\n\n      .. code-block:: python\n\n         evening_events = Event.select().where(Event.time.hour > 17)\n\n   .. attribute:: minute\n\n      Reference the minute of the value stored in the column in a query.\n\n   .. attribute:: second\n\n      Reference the second of the value stored in the column in a query.\n\n.. class:: TimestampField(resolution=1, utc=False, **kwargs)\n\n   :param resolution: Can be provided as either a power of 10, or as an\n       exponent indicating how many decimal places to store.\n   :param bool utc: Treat timestamps as UTC.\n\n   Field class for storing date-times as integer timestamps. Sub-second\n   resolution is supported by multiplying by a power of 10 to get an integer.\n\n   If the ``resolution`` parameter is ``0`` *or* ``1``, then the timestamp is\n   stored using second resolution. A resolution between ``2`` and ``6`` is\n   treated as the number of decimal places, e.g. ``resolution=3`` corresponds\n   to milliseconds. Alternatively, the decimal can be provided as a multiple\n   of 10, such that ``resolution=10`` will store 1/10th of a second\n   resolution.\n\n   The ``resolution`` parameter can be either 0-6 *or* 10, 100, etc up to\n   1000000 (for microsecond resolution). This allows sub-second precision\n   while still using an :class:`IntegerField` for storage. The default is\n   second resolution.\n\n   Also accepts a boolean parameter ``utc``, used to indicate whether the\n   timestamps should be UTC. Default is ``False``.\n\n   Finally, the field ``default`` is the current timestamp. If you do not want\n   this behavior, then explicitly pass in ``default=None``.\n\n.. class:: IPField\n\n   Field class for storing IPv4 addresses efficiently (as integers).\n\n.. class:: BooleanField\n\n   Field class for storing boolean values.\n\n.. class:: BareField(coerce=None, **kwargs)\n\n   :param coerce: Optional function to use for converting raw values into a\n       specific format.\n\n   Field class that does not specify a data-type (**SQLite-only**).\n\n   Since data-types are not enforced, you can declare fields without *any*\n   data-type. It is also common for SQLite virtual tables to use meta-columns\n   or untyped columns, so for those cases as well you may wish to use an\n   untyped field.\n\n   Accepts a special ``coerce`` parameter, a function that takes a value\n   coming from the database and converts it into the appropriate Python type.\n\n.. class:: ForeignKeyField(model, field=None, backref=None, on_delete=None, on_update=None, deferrable=None, object_id_name=None, lazy_load=True, constraint_name=None, **kwargs)\n\n   :param Model model: Model to reference or the string 'self' if declaring a\n       self-referential foreign key.\n   :param Field field: Field to reference on ``model`` (default is primary\n       key).\n   :param str backref: Accessor name for back-reference, or \"+\" to disable\n       the back-reference accessor.\n   :param str on_delete: ON DELETE action, e.g. ``'CASCADE'``..\n   :param str on_update: ON UPDATE action.\n   :param str deferrable: Control when constraint is enforced, e.g. ``'INITIALLY DEFERRED'``.\n   :param str object_id_name: Name for object-id accessor.\n   :param bool lazy_load: Fetch the related object when the foreign-key field\n       attribute is accessed (if it was not already loaded). If this is\n       disabled, accessing the foreign-key field will return the value stored\n       in the foreign-key column.\n   :param str constraint_name: (optional) name to use for foreign-key constraint.\n\n   Field class for storing a foreign key.\n\n   .. code-block:: python\n\n      class User(Model):\n          name = TextField()\n\n      class Tweet(Model):\n          user = ForeignKeyField(User, backref='tweets')\n          content = TextField()\n\n      # \"user\" attribute\n      >>> some_tweet.user\n      <User: charlie>\n\n      # \"tweets\" backref attribute\n      >>> for tweet in charlie.tweets:\n      ...     print(tweet.content)\n      Some tweet\n      Another tweet\n      Yet another tweet\n\n   For an in-depth discussion of foreign-keys, joins and relationships between\n   models, refer to :ref:`relationships`.\n\n   Foreign keys do not have a particular ``field_type`` as they will take\n   their field type depending on the type of primary key on the model they\n   are related to.\n\n   If you manually specify a ``field``, that field must be either a\n   primary key or have a unique constraint.\n\n   .. note::\n      Take care with foreign keys in SQLite. By default, ON DELETE has no\n      effect, which can have surprising (and usually unwanted) effects on\n      your database integrity. This can affect you even if you don't specify\n      ``on_delete``, since the default ON DELETE behaviour (to fail without\n      modifying your data) does not happen, and your data can be silently\n      relinked. The safest thing to do is to specify\n      ``pragmas={'foreign_keys': 1}`` when you instantiate\n      :class:`SqliteDatabase`.\n\n.. class:: DeferredForeignKey(rel_model_name, **kwargs)\n\n   :param str rel_model_name: Model name to reference.\n\n   Field class for representing a deferred foreign key. Useful for circular\n   foreign-key references, for example:\n\n   .. code-block:: python\n\n      class Husband(Model):\n          name = TextField()\n          wife = DeferredForeignKey('Wife', deferrable='INITIALLY DEFERRED')\n\n      class Wife(Model):\n          name = TextField()\n          husband = ForeignKeyField(Husband, deferrable='INITIALLY DEFERRED')\n\n   In the above example, when the ``Wife`` model is declared, the foreign-key\n   ``Husband.wife`` is automatically resolved and turned into a regular\n   :class:`ForeignKeyField`.\n\n   :class:`DeferredForeignKey` references are resolved when model\n   classes are declared and created. This means that if you declare a\n   :class:`DeferredForeignKey` to a model class that has already been\n   imported and created, the deferred foreign key instance will never be\n   resolved. For example:\n\n   .. code-block:: python\n\n      class User(Model):\n          username = TextField()\n\n      class Tweet(Model):\n          # This will never actually be resolved, because the User\n          # model has already been declared.\n          user = DeferredForeignKey('user', backref='tweets')\n          content = TextField()\n\n   In cases like these you should use the regular\n   :class:`ForeignKeyField` *or* you can manually resolve deferred\n   foreign keys like so:\n\n   .. code-block:: python\n\n      # Tweet.user will be resolved into a ForeignKeyField:\n      DeferredForeignKey.resolve(User)\n\n.. class:: ManyToManyField(model, backref=None, through_model=None, on_delete=None, on_update=None, prevent_unsaved=True)\n\n   :param Model model: Model to create relationship with.\n   :param str backref: Accessor name for back-reference\n   :param Model through_model: :class:`Model` to use for the intermediary\n      table. If not provided, a simple through table will be automatically\n      created.\n   :param str on_delete: ON DELETE action, e.g. ``'CASCADE'``. Will be used\n      for foreign-keys in through model.\n   :param str on_update: ON UPDATE action. Will be used for foreign-keys in\n      through model.\n   :param bool prevent_unsaved: Raise ``ValueError`` if accessing relation from\n      an unsaved model instance (default True).\n\n   The :class:`ManyToManyField` provides a simple interface for working\n   with many-to-many relationships, inspired by Django. A many-to-many\n   relationship is typically implemented by creating a junction table with\n   foreign keys to the two models being related. For instance, if you were\n   building a syllabus manager for college students, the relationship between\n   students and courses would be many-to-many. Here is the schema using\n   standard APIs:\n\n   .. attention::\n      This is not a field in the sense that there is no column associated\n      with it. Rather, it provides a convenient interface for accessing rows\n      of data related via a through model.\n\n   Standard way of declaring a many-to-many relationship (without the use of\n   the :class:`ManyToManyField`):\n\n   .. code-block:: python\n\n      class Student(Model):\n          name = CharField()\n\n      class Course(Model):\n          name = CharField()\n\n      class StudentCourse(Model):\n          student = ForeignKeyField(Student)\n          course = ForeignKeyField(Course)\n\n   To query the courses for a particular student, you would join through the\n   junction table:\n\n   .. code-block:: python\n\n      # List the courses that \"Huey\" is enrolled in:\n      courses = (Course\n                 .select()\n                 .join(StudentCourse)\n                 .join(Student)\n                 .where(Student.name == 'Huey'))\n      for course in courses:\n          print(course.name)\n\n   The :class:`ManyToManyField` is designed to simplify this use-case by\n   providing a *field-like* API for querying and modifying data in the\n   junction table. Here is how our code looks using\n   :class:`ManyToManyField`:\n\n   .. code-block:: python\n\n      class Student(Model):\n          name = CharField()\n\n      class Course(Model):\n          name = CharField()\n          students = ManyToManyField(Student, backref='courses')\n\n   It does not matter from Peewee's perspective which model the\n   :class:`ManyToManyField` goes on, since the back-reference is just\n   the mirror image. In order to write valid Python, though, you will need\n   to add the ``ManyToManyField`` on the second model so that the name of\n   the first model is in the scope.\n\n   We still need a junction table to store the relationships between students\n   and courses. This model can be accessed by calling the\n   :meth:`~ManyToManyField.get_through_model` method. This is useful when\n   creating tables.\n\n   .. code-block:: python\n\n      # Create tables for the students, courses, and relationships between\n      # the two.\n      db.create_tables([\n          Student,\n          Course,\n          Course.students.get_through_model()])\n\n   When accessed from a model instance, the :class:`ManyToManyField`\n   exposes a :class:`ModelSelect` representing the set of related objects.\n   Let's use the interactive shell to see how all this works:\n\n   .. code-block:: pycon\n\n      >>> huey = Student.get(Student.name == 'huey')\n      >>> [course.name for course in huey.courses]\n      ['English 101', 'CS 101']\n\n      >>> engl_101 = Course.get(Course.name == 'English 101')\n      >>> [student.name for student in engl_101.students]\n      ['Huey', 'Mickey', 'Zaizee']\n\n   To add new relationships between objects, you can either assign the objects\n   directly to the ``ManyToManyField`` attribute, or call the\n   :meth:`~ManyToManyField.add` method. The difference between the two is\n   that simply assigning will clear out any existing relationships, whereas\n   ``add()`` can preserve existing relationships.\n\n   .. code-block:: pycon\n\n      >>> huey.courses = Course.select().where(Course.name.contains('english'))\n      >>> for course in huey.courses.order_by(Course.name):\n      ...     print(course.name)\n      English 101\n      English 151\n      English 201\n      English 221\n\n      >>> cs_101 = Course.get(Course.name == 'CS 101')\n      >>> cs_151 = Course.get(Course.name == 'CS 151')\n      >>> huey.courses.add([cs_101, cs_151])\n      >>> [course.name for course in huey.courses.order_by(Course.name)]\n      ['CS 101', 'CS151', 'English 101', 'English 151', 'English 201',\n       'English 221']\n\n   This is quite a few courses, so let's remove the 200-level english courses.\n   To remove objects, use the :meth:`~ManyToManyField.remove` method.\n\n   .. code-block:: pycon\n\n      >>> huey.courses.remove(Course.select().where(Course.name.contains('2'))\n      2\n      >>> [course.name for course in huey.courses.order_by(Course.name)]\n      ['CS 101', 'CS151', 'English 101', 'English 151']\n\n   To remove all relationships from a collection, you can use the\n   :meth:`~SelectQuery.clear` method. Let's say that English 101 is\n   canceled, so we need to remove all the students from it:\n\n   .. code-block:: pycon\n\n      >>> engl_101 = Course.get(Course.name == 'English 101')\n      >>> engl_101.students.clear()\n\n   For an overview of implementing many-to-many relationships using\n   standard Peewee APIs, check out the :ref:`manytomany` section. For all\n   but the most simple cases, you will be better off implementing\n   many-to-many using the standard APIs.\n\n   .. attribute:: through_model\n\n      The :class:`Model` representing the many-to-many junction table.\n      Will be auto-generated if not explicitly declared.\n\n   .. method:: add(value, clear_existing=False)\n\n      :param value: Either a :class:`Model` instance, a list of model\n          instances, or a :class:`SelectQuery`.\n      :param bool clear_existing: Whether to remove existing relationships.\n\n      Associate ``value`` with the current instance. You can pass in a single\n      model instance, a list of model instances, or even a :class:`ModelSelect`.\n\n      Example code:\n\n      .. code-block:: python\n\n         # Huey needs to enroll in a bunch of courses, including all\n         # the English classes, and a couple Comp-Sci classes.\n         huey = Student.get(Student.name == 'Huey')\n\n         # We can add all the objects represented by a query.\n         english_courses = Course.select().where(\n             Course.name.contains('english'))\n         huey.courses.add(english_courses)\n\n         # We can also add lists of individual objects.\n         cs101 = Course.get(Course.name == 'CS 101')\n         cs151 = Course.get(Course.name == 'CS 151')\n         huey.courses.add([cs101, cs151])\n\n   .. method:: remove(value)\n\n      :param value: Either a :class:`Model` instance, a list of model\n          instances, or a :class:`ModelSelect`.\n\n      Disassociate ``value`` from the current instance. Like\n      :meth:`~ManyToManyField.add`, you can pass in a model instance, a\n      list of model instances, or even a :class:`ModelSelect`.\n\n      Example code:\n\n      .. code-block:: python\n\n         # Huey is currently enrolled in a lot of english classes\n         # as well as some Comp-Sci. He is changing majors, so we\n         # will remove all his courses.\n         english_courses = Course.select().where(\n             Course.name.contains('english'))\n         huey.courses.remove(english_courses)\n\n         # Remove the two Comp-Sci classes Huey is enrolled in.\n         cs101 = Course.get(Course.name == 'CS 101')\n         cs151 = Course.get(Course.name == 'CS 151')\n         huey.courses.remove([cs101, cs151])\n\n   .. method:: clear()\n\n      Remove all associated objects.\n\n      Example code:\n\n      .. code-block:: python\n\n         # English 101 is canceled this semester, so remove all\n         # the enrollments.\n         english_101 = Course.get(Course.name == 'English 101')\n         english_101.students.clear()\n\n   .. method:: get_through_model()\n\n      Return the :class:`Model` representing the many-to-many junction\n      table. This can be specified manually when the field is being\n      instantiated using the ``through_model`` parameter. If a\n      ``through_model`` is not specified, one will automatically be created.\n\n      When creating tables for an application that uses\n      :class:`ManyToManyField`, **you must explicitly create the through table**.\n\n      .. code-block:: python\n\n         # Get a reference to the automatically-created through table.\n         StudentCourseThrough = Course.students.get_through_model()\n\n         # Create tables for our two models as well as the through model.\n         db.create_tables([\n             Student,\n             Course,\n             StudentCourseThrough])\n\n.. class:: DeferredThroughModel()\n\n   Place-holder for a through-model in cases where, due to a dependency, you\n   cannot declare either a model or a many-to-many field without introducing\n   NameErrors.\n\n   Example:\n\n   .. code-block:: python\n\n      class Note(BaseModel):\n          content = TextField()\n\n      NoteThroughDeferred = DeferredThroughModel()\n\n      class User(BaseModel):\n          username = TextField()\n          notes = ManyToManyField(Note, through_model=NoteThroughDeferred)\n\n      # Cannot declare this before \"User\" since it has a foreign-key to\n      # the User model.\n      class NoteThrough(BaseModel):\n          note = ForeignKeyField(Note)\n          user = ForeignKeyField(User)\n\n      # Resolve dependencies.\n      NoteThroughDeferred.set_model(NoteThrough)\n\n.. class:: CompositeKey(*field_names)\n\n   :param field_names: Names of fields that comprise the primary key.\n\n   A primary key composed of multiple columns. Unlike the other fields, a\n   composite key is defined in the model's ``Meta`` class after the fields\n   have been defined. It takes as parameters the string names of the fields to\n   use as the primary key:\n\n   .. code-block:: python\n\n      class BlogTagThrough(Model):\n          blog = ForeignKeyField(Blog, backref='tags')\n          tag = ForeignKeyField(Tag, backref='blogs')\n\n          class Meta:\n              primary_key = CompositeKey('blog', 'tag')\n\n   See :ref:`composite-keys` for additional discussion.\n\n\n.. _schema-manager-api:\n\nSchema Manager\n--------------\n\n.. class:: SchemaManager(model, database=None, **context_options)\n\n   :param Model model: Model class.\n   :param Database database: if ``None`` defaults to model._meta.database.\n\n   Provides methods for managing the creation and deletion of tables and\n   indexes for the given model.\n\n   .. method:: create_table(safe=True, **options)\n\n      :param bool safe: Specify IF NOT EXISTS clause.\n      :param options: Arbitrary options.\n\n      Execute CREATE TABLE query for the given model.\n\n   .. method:: drop_table(safe=True, drop_sequences=True, **options)\n\n      :param bool safe: Specify IF EXISTS clause.\n      :param bool drop_sequences: Drop any sequences associated with the\n          columns on the table (postgres only).\n      :param options: Arbitrary options.\n\n      Execute DROP TABLE query for the given model.\n\n   .. method:: truncate_table(restart_identity=False, cascade=False)\n\n      :param bool restart_identity: Restart the id sequence (postgres-only).\n      :param bool cascade: Truncate related tables as well (postgres-only).\n\n      Execute TRUNCATE TABLE for the given model. If the database is Sqlite,\n      which does not support TRUNCATE, then an equivalent DELETE query will\n      be executed.\n\n   .. method:: create_indexes(safe=True)\n\n      :param bool safe: Specify IF NOT EXISTS clause.\n\n      Execute CREATE INDEX queries for the indexes defined for the model.\n\n   .. method:: drop_indexes(safe=True)\n\n      :param bool safe: Specify IF EXISTS clause.\n\n      Execute DROP INDEX queries for the indexes defined for the model.\n\n   .. method:: create_sequence(field)\n\n      :param Field field: Field instance which specifies a sequence.\n\n      Create sequence for the given :class:`Field`.\n\n   .. method:: drop_sequence(field)\n\n      :param Field field: Field instance which specifies a sequence.\n\n      Drop sequence for the given :class:`Field`.\n\n   .. method:: create_foreign_key(field)\n\n      :param ForeignKeyField field: Foreign-key field constraint to add.\n\n      Add a foreign-key constraint for the given field. This method should\n      not be necessary in most cases, as foreign-key constraints are created\n      as part of table creation. The exception is when you are creating a\n      circular foreign-key relationship using :class:`DeferredForeignKey`.\n      In those cases, it is necessary to first create the tables, then add\n      the constraint for the deferred foreign-key:\n\n      .. code-block:: python\n\n         class Language(Model):\n             name = TextField()\n             selected_snippet = DeferredForeignKey('Snippet')\n\n         class Snippet(Model):\n             code = TextField()\n             language = ForeignKeyField(Language, backref='snippets')\n\n         # Creates both tables but does not create the constraint for the\n         # Language.selected_snippet foreign key (because of the circular\n         # dependency).\n         db.create_tables([Language, Snippet])\n\n         # Explicitly create the constraint:\n         Language._schema.create_foreign_key(Language.selected_snippet)\n\n      For more information, see documentation on :ref:`circular-fks`.\n\n      Because SQLite has limited support for altering existing tables, it\n      is not possible to add a foreign-key constraint to an existing\n      SQLite table.\n\n   .. method:: create_all(safe=True, **table_options)\n\n      :param bool safe: Whether to specify IF NOT EXISTS.\n\n      Create sequence(s), index(es) and table for the model.\n\n   .. method:: drop_all(safe=True, drop_sequences=True, **options)\n\n      :param bool safe: Whether to specify IF EXISTS.\n      :param bool drop_sequences: Drop any sequences associated with the\n          columns on the table (postgres only).\n      :param options: Arbitrary options.\n\n      Drop table for the model and associated indexes.\n\n\n.. class:: Index(name, table, expressions, unique=False, safe=False, where=None, using=None, nulls_distinct=None)\n\n   :param str name: Index name.\n   :param Table table: Table to create index on.\n   :param expressions: List of columns to index on (or expressions).\n   :param bool unique: Whether index is UNIQUE.\n   :param bool safe: Whether to add IF NOT EXISTS clause.\n   :param Expression where: Optional WHERE clause for index.\n   :param str using: Index algorithm.\n   :param bool nulls_distinct: Postgres-only - specify True (NULLS DISTINCT)\n       or False (NULLS NOT DISTINCT) - controls handling of NULL in unique\n       indexes.\n\n   .. method:: safe(_safe=True)\n\n      :param bool _safe: Whether to add IF NOT EXISTS clause.\n\n   .. method:: where(*expressions)\n\n      :param expressions: zero or more expressions to include in the WHERE\n          clause.\n\n      Include the given expressions in the WHERE clause of the index. The\n      expressions will be AND-ed together with any previously-specified\n      WHERE expressions.\n\n   .. method:: using(_using=None)\n\n      :param str _using: Specify index algorithm for USING clause.\n\n   .. method:: nulls_distinct(nulls_distinct=None)\n\n      :param bool nulls_distinct: specify True (NULLS DISTINCT) or False\n          for (NULLS NOT DISTINCT).\n\n      Requires Postgres 15 or newer.\n\n      Control handling of NULL values in unique indexes.\n\n\n.. class:: ModelIndex(model, fields, unique=False, safe=True, where=None, using=None, name=None, nulls_distinct=None)\n\n   :param Model model: Model class to create index on.\n   :param list fields: Fields to index.\n   :param bool unique: Whether index is UNIQUE.\n   :param bool safe: Whether to add IF NOT EXISTS clause.\n   :param Expression where: Optional WHERE clause for index.\n   :param str using: Index algorithm or type, e.g. 'BRIN', 'GiST' or 'GIN'.\n   :param str name: Optional index name.\n   :param bool nulls_distinct: Postgres-only - specify True (NULLS DISTINCT)\n       or False (NULLS NOT DISTINCT) - controls handling of NULL in unique\n       indexes.\n\n   Expressive method for declaring an index on a model.\n\n   Examples:\n\n   .. code-block:: python\n\n      class Article(Model):\n          name = TextField()\n          timestamp = TimestampField()\n          status = IntegerField()\n          flags = BitField()\n\n          is_sticky = flags.flag(1)\n          is_favorite = flags.flag(2)\n\n      # CREATE INDEX ... ON \"article\" (\"name\", \"timestamp\")\n      idx = ModelIndex(Article, (Article.name, Article.timestamp))\n\n      # CREATE INDEX ... ON \"article\" (\"name\", \"timestamp\") WHERE \"status\" = 1\n      idx = idx.where(Article.status == 1)\n\n      # CREATE UNIQUE INDEX ... ON \"article\" (\"timestamp\" DESC, \"flags\" & 2) WHERE \"status\" = 1\n      idx = ModelIndex(\n          Article,\n          (Article.timestamp.desc(), Article.flags.bin_and(2)),\n          unique = True).where(Article.status == 1)\n\n   You can also use :meth:`Model.index`:\n\n   .. code-block:: python\n\n      idx = Article.index(Article.name, Article.timestamp).where(Article.status == 1)\n\n   To add an index to a model definition use :meth:`Model.add_index`:\n\n   .. code-block:: python\n\n      idx = Article.index(Article.name, Article.timestamp).where(Article.status == 1)\n\n      # Add above index definition to the model definition. When you call\n      # Article.create_table() (or database.create_tables([Article])), the\n      # index will be created.\n      Article.add_index(idx)\n\n\n.. _query-builder-api:\n\nQuery-builder\n-------------\n\n.. seealso: :ref:`query-builder`\n\n.. class:: Node()\n\n   Base-class for all components which make up the AST for a SQL query.\n\n   .. staticmethod:: copy(method)\n\n      Decorator to use with Node methods that mutate the node's state.\n      This allows method-chaining, e.g.:\n\n      .. code-block:: python\n\n          query = MyModel.select()\n          new_query = query.where(MyModel.field == 'value')\n\n   .. method:: unwrap()\n\n      API for recursively unwrapping \"wrapped\" nodes. Base case is to\n      return self.\n\n   .. method:: is_alias()\n\n      API for determining if a node, at any point, has been explicitly\n      aliased by the user.\n\n\n.. class:: Source(alias=None)\n\n   A source of row tuples, for example a table, join, or select query. By\n   default provides a \"magic\" attribute named \"c\" that is a factory for\n   column/attribute lookups, for example:\n\n   .. code-block:: python\n\n      User = Table('users')\n      query = (User\n               .select(User.c.username)\n               .where(User.c.active == True)\n               .order_by(User.c.username))\n\n   .. method:: alias(name)\n\n      Returns a copy of the object with the given alias applied.\n\n   .. method:: select(*columns)\n\n      :param columns: :class:`Column` instances, expressions, functions,\n          sub-queries, or anything else that you would like to select.\n\n      Create a :class:`Select` query on the table. If the table explicitly\n      declares columns and no columns are provided, then by default all the\n      table's defined columns will be selected.\n\n   .. method:: join(dest, join_type='INNER', on=None)\n\n      :param Source dest: Join the table with the given destination.\n      :param str join_type: Join type.\n      :param on: Expression to use as join predicate.\n      :return: a :class:`Join` instance.\n\n      Join type may be one of:\n\n      * ``JOIN.INNER``\n      * ``JOIN.LEFT_OUTER``\n      * ``JOIN.RIGHT_OUTER``\n      * ``JOIN.FULL``\n      * ``JOIN.FULL_OUTER``\n      * ``JOIN.CROSS``\n\n   .. method:: left_outer_join(dest, on=None)\n\n      :param Source dest: Join the table with the given destination.\n      :param on: Expression to use as join predicate.\n      :return: a :class:`Join` instance.\n\n      Convenience method for calling :meth:`~Source.join` using a LEFT\n      OUTER join.\n\n\n.. class:: BaseTable()\n\n   Base class for table-like objects, which support JOINs via operator\n   overloading.\n\n   .. method:: __and__(dest)\n\n       Perform an INNER join on ``dest``.\n\n   .. method:: __add__(dest)\n\n       Perform a LEFT OUTER join on ``dest``.\n\n   .. method:: __sub__(dest)\n\n       Perform a RIGHT OUTER join on ``dest``.\n\n   .. method:: __or__(dest)\n\n       Perform a FULL OUTER join on ``dest``.\n\n   .. method:: __mul__(dest)\n\n       Perform a CROSS join on ``dest``.\n\n\n.. class:: Table(name, columns=None, primary_key=None, schema=None, alias=None)\n\n   Represents a table in the database (or a table-like object such as a view).\n\n   :param str name: Database table name\n   :param tuple columns: List of column names (optional).\n   :param str primary_key: Name of primary key column.\n   :param str schema: Schema name used to access table (if necessary).\n   :param str alias: Alias to use for table in SQL queries.\n\n   If columns are specified, the magic \"c\" attribute will be disabled.\n\n   When columns are not explicitly defined, tables have a special attribute\n   \"c\" which is a factory that provides access to table columns dynamically.\n\n   Example:\n\n   .. code-block:: python\n\n      User = Table('users')\n      query = (User\n               .select(User.c.id, User.c.username)\n               .order_by(User.c.username))\n\n   Equivalent example when columns **are** specified:\n\n   .. code-block:: python\n\n      User = Table('users', ('id', 'username'))\n      query = (User\n               .select(User.id, User.username)\n               .order_by(User.username))\n\n   .. method:: bind(database=None)\n\n      :param database: :class:`Database` object.\n\n      Bind this table to the given database (or unbind by leaving empty).\n\n      When a table is *bound* to a database, queries may be executed against\n      it without the need to specify the database in the query's execute\n      method.\n\n   .. method:: bind_ctx(database=None)\n\n      :param database: :class:`Database` object.\n\n      Return a context manager that will bind the table to the given database\n      for the duration of the wrapped block.\n\n   .. method:: select(*columns)\n\n      :param columns: :class:`Column` instances, expressions, functions,\n          sub-queries, or anything else that you would like to select.\n\n      Create a :class:`Select` query on the table. If the table explicitly\n      declares columns and no columns are provided, then by default all the\n      table's defined columns will be selected.\n\n      Examples:\n\n      .. code-block:: python\n\n         User = Table('users', ('id', 'username'))\n\n         # Because columns were defined on the Table, we will default to\n         # selecting both of the User table's columns.\n         # Evaluates to SELECT id, username FROM users\n         query = User.select()\n\n         Note = Table('notes')\n         query = (Note\n                  .select(Note.c.content, Note.c.timestamp, User.username)\n                  .join(User, on=(Note.c.user_id == User.id))\n                  .where(Note.c.is_published == True)\n                  .order_by(Note.c.timestamp.desc()))\n\n         # Using a function to select users and the number of notes they\n         # have authored.\n         query = (User\n                  .select(\n                     User.username,\n                     fn.COUNT(Note.c.id).alias('n_notes'))\n                  .join(\n                     Note,\n                     JOIN.LEFT_OUTER,\n                     on=(User.id == Note.c.user_id))\n                  .order_by(fn.COUNT(Note.c.id).desc()))\n\n   .. method:: insert(insert=None, columns=None, **kwargs)\n\n      :param insert: A dictionary mapping column to value, an iterable that\n          yields dictionaries (i.e. list), or a :class:`Select` query.\n      :param list columns: The list of columns to insert into when the\n          data being inserted is not a dictionary.\n      :param kwargs: Mapping of column-name to value.\n\n      Create a :class:`Insert` query into the table.\n\n   .. method:: replace(insert=None, columns=None, **kwargs)\n\n      :param insert: A dictionary mapping column to value, an iterable that\n          yields dictionaries (i.e. list), or a :class:`Select` query.\n      :param list columns: The list of columns to insert into when the\n          data being inserted is not a dictionary.\n      :param kwargs: Mapping of column-name to value.\n\n      Create a :class:`Insert` query into the table whose conflict\n      resolution method is to replace.\n\n   .. method:: update(update=None, **kwargs)\n\n      :param update: A dictionary mapping column to value.\n      :param kwargs: Mapping of column-name to value.\n\n      Create a :class:`Update` query for the table.\n\n   .. method:: delete()\n\n      Create a :class:`Delete` query for the table.\n\n\n.. class:: Join(lhs, rhs, join_type=JOIN.INNER, on=None, alias=None)\n\n   Represent a JOIN between two table-like objects.\n\n   :param lhs: Left-hand side of the join.\n   :param rhs: Right-hand side of the join.\n   :param join_type: Type of join. e.g. JOIN.INNER, JOIN.LEFT_OUTER, etc.\n   :param on: Expression describing the join predicate.\n   :param str alias: Alias to apply to joined data.\n\n   .. method:: on(predicate)\n\n       :param Expression predicate: join predicate.\n\n       Specify the predicate expression used for this join.\n\n\n.. class:: ValuesList(values, columns=None, alias=None)\n\n   Represent a values list that can be used like a table.\n\n   :param values: a list-of-lists containing the row data to represent.\n   :param list columns: the names to give to the columns in each row.\n   :param str alias: alias to use for values-list.\n\n   Example:\n\n   .. code-block:: python\n\n      data = [(1, 'first'), (2, 'second')]\n      vl = ValuesList(data, columns=('idx', 'name'))\n\n      query = (vl\n               .select(vl.c.idx, vl.c.name)\n               .order_by(vl.c.idx))\n      # Yields:\n      # SELECT t1.idx, t1.name\n      # FROM (VALUES (1, 'first'), (2, 'second')) AS t1(idx, name)\n      # ORDER BY t1.idx\n\n   .. method:: columns(*names)\n\n      :param names: names to apply to the columns of data.\n\n      Example:\n\n      .. code-block:: python\n\n         vl = ValuesList([(1, 'first'), (2, 'second')])\n         vl = vl.columns('idx', 'name').alias('v')\n\n         query = vl.select(vl.c.idx, vl.c.name)\n         # Yields:\n         # SELECT v.idx, v.name\n         # FROM (VALUES (1, 'first'), (2, 'second')) AS v(idx, name)\n\n\n.. class:: CTE(name, query, recursive=False, columns=None)\n\n   Represent a common-table-expression. For example queries, see :ref:`cte`.\n\n   :param name: Name for the CTE.\n   :param query: :class:`Select` query describing CTE.\n   :param bool recursive: Whether the CTE is recursive.\n   :param list columns: Explicit list of columns produced by CTE (optional).\n\n   .. method:: select_from(*columns)\n\n      Create a SELECT query that utilizes the given common table expression\n      as the source for a new query.\n\n      :param columns: One or more columns to select from the CTE.\n      :return: :class:`Select` query utilizing the common table expression\n\n   .. method:: union_all(other)\n\n      Used on the base-case CTE to construct the recursive term of the CTE.\n\n      :param other: recursive term, generally a :class:`Select` query.\n      :return: a recursive :class:`CTE` with the given recursive term.\n\n\n.. class:: ColumnBase()\n\n   Base-class for column-like objects, attributes or expressions.\n\n   Column-like objects can be composed using various operators and special\n   methods.\n\n   * ``&``: Logical AND\n   * ``|``: Logical OR\n   * ``+``: Addition\n   * ``-``: Subtraction\n   * ``*``: Multiplication\n   * ``/``: Division\n   * ``^``: Exclusive-OR\n   * ``==``: Equality\n   * ``!=``: Inequality\n   * ``>``: Greater-than\n   * ``<``: Less-than\n   * ``>=``: Greater-than or equal\n   * ``<=``: Less-than or equal\n   * ``<<``: ``IN``\n   * ``>>``: ``IS`` (i.e. ``IS NULL``)\n   * ``%``: ``LIKE``\n   * ``**``: ``ILIKE``\n   * ``bin_and()``: Binary AND\n   * ``bin_or()``: Binary OR\n   * ``in_()``: ``IN``\n   * ``not_in()``: ``NOT IN``\n   * ``regexp()``: ``REGEXP``\n   * ``is_null(True/False)``: ``IS NULL`` or ``IS NOT NULL``\n   * ``contains(s)``: ``LIKE %s%``\n   * ``startswith(s)``: ``LIKE s%``\n   * ``endswith(s)``: ``LIKE %s``\n   * ``between(low, high)``: ``BETWEEN low AND high``\n   * ``concat()``: ``||``\n\n   .. method:: alias(alias)\n\n      :param str alias: Alias for the given column-like object.\n      :return: a :class:`Alias` object.\n\n      Indicate the alias that should be given to the specified column-like\n      object.\n\n   .. method:: cast(as_type)\n\n      :param str as_type: Type name to cast to.\n      :return: a :class:`Cast` object.\n\n      Create a ``CAST`` expression.\n\n      Example:\n\n      .. code-block:: python\n\n         # Cast a text column to integer for comparison.\n         query = (Entry\n                  .select()\n                  .where(Entry.legacy_code.cast('INTEGER') > 100))\n\n   .. method:: asc(collation=None, nulls=None)\n\n      :param str collation: Collation name to use for sorting.\n      :param str nulls: Sort nulls (FIRST or LAST).\n      :return: an ascending :class:`Ordering` object for the column.\n\n   .. method:: desc(collation=None, nulls=None)\n\n      :param str collation: Collation name to use for sorting.\n      :param str nulls: Sort nulls (FIRST or LAST).\n      :return: an descending :class:`Ordering` object for the column.\n\n   .. method:: __invert__()\n\n      :return: a :class:`Negated` wrapper for the column.\n\n\n.. class:: Column(source, name)\n\n   :param Source source: Source for column.\n   :param str name: Column name.\n\n   Column on a table or a column returned by a sub-query.\n\n\n.. class:: Alias(node, alias)\n\n   :param Node node: a column-like object.\n   :param str alias: alias to assign to column.\n\n   Create a named alias for the given column-like object.\n\n   .. method:: alias(alias=None)\n\n      :param str alias: new name (or None) for aliased column.\n\n      Create a new :class:`Alias` for the aliased column-like object. If\n      the new alias is ``None``, then the original column-like object is\n      returned.\n\n\n.. class:: Negated(node)\n\n   Represents a negated column-like object.\n\n\n.. class:: Value(value, converter=None, unpack=True)\n\n   :param value: Python object or scalar value.\n   :param converter: Function used to convert value into type the database\n       understands.\n   :param bool unpack: Whether lists or tuples should be unpacked into a list\n       of values or treated as-is.\n\n   Value to be used in a parameterized query. It is the responsibility of the\n   caller to ensure that the value passed in can be adapted to a type the\n   database driver understands.\n\n\n.. function:: AsIs(value, converter=None)\n\n   Represents a :class:`Value` that is treated as-is, and passed directly\n   back to the database driver. This may be useful if you are using database\n   extensions that accept native Python data-types and you do not wish Peewee\n   to impose any handling of the values.\n\n   In the event a converter is in scope for this value, the converter will be\n   applied unless ``converter=False`` (in which case no conversion is applied\n   by Peewee and the value is passed directly to the driver). The Postgres JSON\n   extensions make use of this to pass ``dict`` and ``list`` to the driver,\n   which then handles the JSON serialization more efficiently, for example.\n\n\n.. class:: Cast(node, cast)\n\n   :param node: A column-like object.\n   :param str cast: Type to cast to.\n\n   Represents a ``CAST(<node> AS <cast>)`` expression.\n\n\n.. class:: Ordering(node, direction, collation=None, nulls=None)\n\n   :param node: A column-like object.\n   :param str direction: ASC or DESC\n   :param str collation: Collation name to use for sorting.\n   :param str nulls: Sort nulls (FIRST or LAST).\n\n   Represent ordering by a column-like object.\n\n   Postgresql supports a non-standard clause (\"NULLS FIRST/LAST\"). Peewee will\n   automatically use an equivalent ``CASE`` statement for databases that do\n   not support this (Sqlite / MySQL).\n\n   .. method:: collate(collation=None)\n\n      :param str collation: Collation name to use for sorting.\n\n\n.. function:: Asc(node, collation=None, nulls=None)\n\n   Short-hand for instantiating an ascending :class:`Ordering` object.\n\n\n.. function:: Desc(node, collation=None, nulls=None)\n\n   Short-hand for instantiating an descending :class:`Ordering` object.\n\n\n.. class:: Expression(lhs, op, rhs, flat=True)\n\n   :param lhs: Left-hand side.\n   :param op: Operation.\n   :param rhs: Right-hand side.\n   :param bool flat: Whether to wrap expression in parentheses.\n\n   Represent a binary expression of the form (lhs op rhs), e.g. (foo + 1).\n\n\n.. class:: Entity(*path)\n\n   :param path: Components that make up the dotted-path of the entity name.\n\n   Represent a quoted entity in a query, such as a table, column, alias. The\n   name may consist of multiple components, e.g. \"a_table\".\"column_name\".\n\n   .. method:: __getattr__(self, attr)\n\n      Factory method for creating sub-entities.\n\n\n.. class:: SQL(sql, params=None)\n\n   :param str sql: SQL query string.\n   :param tuple params: Parameters for query (optional).\n\n   Represent a parameterized SQL query or query-fragment.\n\n\n.. function:: Check(constraint, name=None)\n\n   :param str constraint: Constraint SQL.\n   :param str name: constraint name.\n\n   Represent a CHECK constraint.\n\n   MySQL may not support a ``name`` parameter when inlining the\n   constraint along with the column definition. The solution is to just\n   put the named ``Check`` constraint in the model's ``Meta.constraints``\n   list instead of in the field instances ``constraints=[...]`` list.\n\n\n.. function:: Default(value)\n\n   :param value: default value (literal).\n\n   Represent a DEFAULT constraint. It is important to note that this\n   constraint does not accept a parameterized value, so the value literal must\n   be given. If a string value is intended, it must be quoted.\n\n   Examples:\n\n   .. code-block:: python\n\n     # \"added\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP.\n     added = DateTimeField(constraints=[Default('CURRENT_TIMESTAMP')])\n\n     # \"label\" TEXT NOT NULL DEFAULT 'string literal'\n     label = TextField(constraints=[Default(\"'string literal'\")])\n\n     # \"status\" INTEGER NOT NULL DEFAULT 0\n     status = IntegerField(constraints=[Default(0)])\n\n\n.. class:: Function(name, arguments, coerce=True, python_value=None)\n\n   :param str name: Function name.\n   :param tuple arguments: Arguments to function.\n   :param bool coerce: Whether to coerce the function result to a particular\n       data-type when reading function return values from the cursor.\n   :param callable python_value: Function to use for converting the return\n       value from the cursor.\n\n   Represent an arbitrary SQL function call.\n\n   Rather than instantiating this class directly, it is recommended to use\n   the ``fn`` helper.\n\n   Example of using ``fn`` to call an arbitrary SQL function:\n\n   .. code-block:: python\n\n      # Query users and count of tweets authored.\n      query = (User\n               .select(User.username, fn.COUNT(Tweet.id).alias('ct'))\n               .join(Tweet, JOIN.LEFT_OUTER, on=(User.id == Tweet.user_id))\n               .group_by(User.username)\n               .order_by(fn.COUNT(Tweet.id).desc()))\n\n   .. method:: over(partition_by=None, order_by=None, start=None, end=None, window=None, exclude=None)\n\n      :param list partition_by: List of columns to partition by.\n      :param list order_by: List of columns / expressions to order window by.\n      :param start: A :class:`SQL` instance or a string expressing the\n          start of the window range.\n      :param end: A :class:`SQL` instance or a string expressing the\n          end of the window range.\n      :param str frame_type: ``Window.RANGE``, ``Window.ROWS`` or\n          ``Window.GROUPS``.\n      :param Window window: A :class:`Window` instance.\n      :param exclude: Frame exclusion, one of ``Window.CURRENT_ROW``,\n          ``Window.GROUP``, ``Window.TIES`` or ``Window.NO_OTHERS``.\n\n      For an in-depth guide to using window functions with Peewee,\n      see the :ref:`window-functions` section.\n\n      Examples:\n\n      .. code-block:: python\n\n         # Using a simple partition on a single column.\n         query = (Sample\n                  .select(\n                     Sample.counter,\n                     Sample.value,\n                     fn.AVG(Sample.value).over([Sample.counter]))\n                  .order_by(Sample.counter))\n\n         # Equivalent example Using a Window() instance instead.\n         window = Window(partition_by=[Sample.counter])\n         query = (Sample\n                  .select(\n                     Sample.counter,\n                     Sample.value,\n                     fn.AVG(Sample.value).over(window))\n                  .window(window)  # Note call to \".window()\"\n                  .order_by(Sample.counter))\n\n         # Example using bounded window.\n         query = (Sample\n                  .select(Sample.value,\n                          fn.SUM(Sample.value).over(\n                             partition_by=[Sample.counter],\n                             start=Window.CURRENT_ROW,  # current row\n                             end=Window.following()))  # unbounded following\n                  .order_by(Sample.id))\n\n   .. method:: filter(where)\n\n      :param where: Expression for filtering aggregate.\n\n      Add a ``FILTER (WHERE...)`` clause to an aggregate function. The where\n      expression is evaluated to determine which rows are fed to the\n      aggregate function. This SQL feature is supported for Postgres and\n      SQLite.\n\n   .. method:: coerce(coerce=True)\n\n      :param bool coerce: Whether to attempt to coerce function-call result\n          to a Python data-type.\n\n      When coerce is ``True``, the target data-type is inferred using several\n      heuristics. Read the source for ``BaseModelCursorWrapper._initialize_columns``\n      method to see how this works.\n\n   .. method:: python_value(func=None)\n\n      :param callable python_value: Function to use for converting the return\n          value from the cursor.\n\n      Specify a particular function to use when converting values returned by\n      the database cursor. For example:\n\n      .. code-block:: python\n\n         # Get user and a list of their tweet IDs. The tweet IDs are\n         # returned as a comma-separated string by the db, so we'll split\n         # the result string and convert the values to python ints.\n         convert_ids = lambda s: [int(i) for i in (s or '').split(',') if i]\n         tweet_ids = (fn\n                      .GROUP_CONCAT(Tweet.id)\n                      .python_value(convert_ids))\n\n         query = (User\n                  .select(User.username, tweet_ids.alias('tweet_ids'))\n                  .join(Tweet)\n                  .group_by(User.username))\n\n         for user in query:\n             print(user.username, user.tweet_ids)\n\n         # e.g.,\n         # huey [1, 4, 5, 7]\n         # mickey [2, 3, 6]\n         # zaizee []\n\n.. function:: fn()\n\n   The :func:`fn` helper is actually an instance of :class:`Function`\n   that implements a ``__getattr__`` hook to provide a nice API for calling\n   SQL functions.\n\n   To create a node representative of a SQL function call, use the function\n   name as an attribute on ``fn`` and then provide the arguments as you would\n   if calling a Python function:\n\n   .. code-block:: python\n\n      # List users and the number of tweets they have authored,\n      # from highest-to-lowest:\n      sql_count = fn.COUNT(Tweet.id)\n      query = (User\n               .select(User, sql_count.alias('count'))\n               .join(Tweet, JOIN.LEFT_OUTER)\n               .group_by(User)\n               .order_by(sql_count.desc()))\n\n      # Get the timestamp of the most recent tweet:\n      query = Tweet.select(fn.MAX(Tweet.timestamp))\n      max_timestamp = query.scalar()  # Retrieve scalar result from query.\n\n   Function calls can, like anything else, be composed and nested:\n\n   .. code-block:: python\n\n      # Get users whose username begins with \"A\" or \"a\":\n      a_users = User.select().where(fn.LOWER(fn.SUBSTR(User.username, 1, 1)) == 'a')\n\n\n.. class:: Window(partition_by=None, order_by=None, start=None, end=None, frame_type=None, extends=None, exclude=None, alias=None)\n\n   :param list partition_by: List of columns to partition by.\n   :param list order_by: List of columns to order by.\n   :param start: A :class:`SQL` instance or a string expressing the start\n       of the window range.\n   :param end: A :class:`SQL` instance or a string expressing the end of\n       the window range.\n   :param str frame_type: ``Window.RANGE``, ``Window.ROWS`` or\n       ``Window.GROUPS``.\n   :param extends: A :class:`Window` definition to extend. Alternately, you\n       may specify the window's alias instead.\n   :param exclude: Frame exclusion, one of ``Window.CURRENT_ROW``,\n       ``Window.GROUP``, ``Window.TIES`` or ``Window.NO_OTHERS``.\n   :param str alias: Alias for the window.\n\n   Represent a WINDOW clause.\n\n   For an in-depth guide to using window functions with Peewee,\n   see the :ref:`window-functions` section.\n\n   .. attribute:: RANGE\n                  ROWS\n                  GROUPS\n\n      Specify the window ``frame_type``. See :ref:`window-frame-types`.\n\n   .. attribute:: CURRENT_ROW\n\n      Reference to current row for use in start/end clause or the frame\n      exclusion parameter.\n\n   .. attribute:: NO_OTHERS\n                  GROUP\n                  TIES\n\n      Specify the window frame exclusion parameter.\n\n   .. staticmethod:: preceding(value=None)\n\n      :param value: Number of rows preceding. If ``None`` is UNBOUNDED.\n\n      Convenience method for generating SQL suitable for passing in as the\n      ``start`` parameter for a window range.\n\n   .. staticmethod:: following(value=None)\n\n      :param value: Number of rows following. If ``None`` is UNBOUNDED.\n\n      Convenience method for generating SQL suitable for passing in as the\n      ``end`` parameter for a window range.\n\n   .. method:: as_rows()\n               as_range()\n               as_groups()\n\n      Specify the frame type.\n\n   .. method:: extends(window=None)\n\n      :param Window window: A :class:`Window` definition to extend.\n          Alternately, you may specify the window's alias instead.\n\n   .. method:: exclude(frame_exclusion=None)\n\n      :param frame_exclusion: Frame exclusion, one of ``Window.CURRENT_ROW``,\n          ``Window.GROUP``, ``Window.TIES`` or ``Window.NO_OTHERS``.\n\n   .. method:: alias(alias=None)\n\n      :param str alias: Alias to use for window.\n\n\n.. function:: Case(predicate, expression_tuples, default=None)\n\n   :param predicate: Predicate for CASE query (optional).\n   :param expression_tuples: One or more cases to evaluate.\n   :param default: Default value (optional).\n   :return: Representation of CASE statement.\n\n   Example:\n\n   .. code-block:: python\n\n      Number = Table('numbers', ('val',))\n\n      num_as_str = Case(Number.val, (\n          (1, 'one'),\n          (2, 'two'),\n          (3, 'three')), 'a lot')\n\n      query = Number.select(Number.val, num_as_str.alias('num_str'))\n\n   Equivalent SQL:\n\n   .. code-block:: sql\n\n      SELECT \"val\",\n        CASE \"val\"\n            WHEN 1 THEN 'one'\n            WHEN 2 THEN 'two'\n            WHEN 3 THEN 'three'\n            ELSE 'a lot' END AS \"num_str\"\n      FROM \"numbers\"\n\n   Example:\n\n   .. code-block:: python\n\n      num_as_str = Case(None, (\n          (Number.val == 1, 'one'),\n          (Number.val == 2, 'two'),\n          (Number.val == 3, 'three')), 'a lot')\n      query = Number.select(Number.val, num_as_str.alias('num_str'))\n\n   Equivalent SQL:\n\n   .. code-block:: sql\n\n      SELECT \"val\",\n        CASE\n            WHEN \"val\" = 1 THEN 'one'\n            WHEN \"val\" = 2 THEN 'two'\n            WHEN \"val\" = 3 THEN 'three'\n            ELSE 'a lot' END AS \"num_str\"\n      FROM \"numbers\"\n\n\n.. class:: ForUpdate(expr, of=None, nowait=None, skip_locked=None)\n\n   :param str expr: Either a boolean (``True``) or a string indicating the\n       desired lock strength, e.g. \"FOR SHARE\" or \"FOR NO KEY UPDATE\".\n   :param of: One or more models to restrict locking to.\n   :param bool nowait: Specify NOWAIT option when locking.\n   :param bool skip_locked: Specify SKIP LOCKED option when locking.\n\n   Represent a ``FOR UPDATE`` SQL clause in a :class:`Select` query. Typically\n   you will call :meth:`Select.for_update` rather than using this class\n   directly.\n\n\n.. class:: NodeList(nodes, glue=' ', parens=False)\n\n   :param list nodes: Zero or more nodes.\n   :param str glue: How to join the nodes when converting to SQL.\n   :param bool parens: Whether to wrap the resulting SQL in parentheses.\n\n   Represent a list of nodes, a multi-part clause, a list of parameters, etc.\n\n\n.. function:: CommaNodeList(nodes)\n\n   :param list nodes: Zero or more nodes.\n   :return: a :class:`NodeList`\n\n   Represent a list of nodes joined by commas.\n\n\n.. function:: EnclosedNodeList(nodes)\n\n   :param list nodes: Zero or more nodes.\n   :return: a :class:`NodeList`\n\n   Represent a list of nodes joined by commas and wrapped in parentheses.\n\n\n.. class:: DQ(**query)\n\n   :param query: Arbitrary filter expressions using Django-style lookups.\n\n   Represent a composable Django-style filter expression suitable for use with\n   the :meth:`Model.filter` or :meth:`ModelSelect.filter` methods.\n\n\n.. class:: Tuple(*args)\n\n   Represent a SQL `row value <https://www.sqlite.org/rowvalue.html>`_.\n   Row-values are supported by most databases.\n\n\n.. class:: OnConflict(action=None, update=None, preserve=None, where=None, conflict_target=None, conflict_where=None, conflict_constraint=None)\n\n   :param str action: Action to take when resolving conflict.\n   :param update: A dictionary mapping column to new value.\n   :param preserve: A list of columns whose values should be preserved from the original INSERT. See also :class:`EXCLUDED`.\n   :param where: Expression to restrict the conflict resolution.\n   :param conflict_target: Column(s) that comprise the constraint.\n   :param conflict_where: Expressions needed to match the constraint target if it is a partial index (index with a WHERE clause).\n   :param str conflict_constraint: Name of constraint to use for conflict\n       resolution. Currently only supported by Postgres.\n\n   Represent a conflict resolution clause for a data-modification query.\n\n   See :ref:`upsert` for detailed discussion.\n\n   .. method:: preserve(*columns)\n\n      :param columns: Columns whose values should be preserved.\n\n   .. method:: update(_data=None, **kwargs)\n\n      :param dict _data: Dictionary mapping column to new value.\n      :param kwargs: Dictionary mapping column name to new value.\n\n      The ``update()`` method supports being called with either a dictionary\n      of column-to-value, **or** keyword arguments representing the same.\n\n   .. method:: where(*expressions)\n\n      :param expressions: Expressions that restrict the action of the\n          conflict resolution clause.\n\n   .. method:: conflict_target(*constraints)\n\n      :param constraints: Column(s) to use as target for conflict resolution.\n\n   .. method:: conflict_where(*expressions)\n\n      :param expressions: Expressions that match the conflict target index,\n          in the case the conflict target is a partial index.\n\n   .. method:: conflict_constraint(constraint)\n\n      :param str constraint: Name of constraints to use as target for\n          conflict resolution. Currently only supported by Postgres.\n\n\n.. class:: EXCLUDED\n\n   Helper object that exposes the ``EXCLUDED`` namespace that is used with\n   ``INSERT ... ON CONFLICT`` to reference values in the conflicting data.\n   This is a \"magic\" helper, such that one uses it by accessing attributes on\n   it that correspond to a particular column.\n\n   See :meth:`Insert.on_conflict` for example usage.\n\n\nQueries\n-------\n\n.. class:: BaseQuery()\n\n   The parent class from which all other query classes are derived. While you\n   will not deal with :class:`BaseQuery` directly in your code, it\n   implements some methods that are common across all query types.\n\n   .. attribute:: default_row_type = ROW.DICT\n\n   .. method:: bind(database=None)\n\n      :param Database database: Database to execute query against.\n\n      Bind the query to the given database for execution.\n\n   .. method:: dicts(as_dict=True)\n\n      :param bool as_dict: Specify whether to return rows as dictionaries.\n\n      Return rows as dictionaries.\n\n   .. method:: tuples(as_tuples=True)\n\n      :param bool as_tuples: Specify whether to return rows as tuples.\n\n      Return rows as tuples.\n\n   .. method:: namedtuples(as_namedtuple=True)\n\n      :param bool as_namedtuple: Specify whether to return rows as named\n          tuples.\n\n      Return rows as named tuples.\n\n   .. method:: objects(constructor=None)\n\n      :param constructor: Function that accepts row dict and returns an\n          arbitrary object.\n\n      Return rows as arbitrary objects using the given constructor.\n\n   .. method:: sql()\n\n      :return: A 2-tuple consisting of the query's SQL and parameters.\n\n   .. method:: execute(database)\n\n      :param Database database: Database to execute query against. Not\n          required if query was previously bound to a database.\n\n      Execute the query and return result (depends on type of query being\n      executed). For example, select queries the return result will be an\n      iterator over the query results.\n\n   .. method:: iterator(database=None)\n\n      :param Database database: Database to execute query against. Not\n          required if query was previously bound to a database.\n\n      Execute the query and return an iterator over the result-set. For large\n      result-sets this method is preferable as rows are not cached in-memory\n      during iteration.\n\n      Because rows are not cached, the query may only be iterated over\n      once. Subsequent iterations will return empty result-sets as the\n      cursor will have been consumed.\n\n      Example:\n\n      .. code-block:: python\n\n         query = StatTbl.select().order_by(StatTbl.timestamp).tuples()\n         for row in query.iterator(db):\n             process_row(row)\n\n   .. method:: __iter__()\n\n      Execute the query and return an iterator over the result-set.\n\n      Unlike :meth:`~BaseQuery.iterator`, this method will cause rows to\n      be cached in order to allow efficient iteration, indexing and slicing.\n\n   .. method:: __getitem__(value)\n\n      :param value: Either an integer index or a slice.\n\n      Retrieve a row or range of rows from the result-set.\n\n   .. method:: __len__()\n\n      Return the number of rows in the result-set.\n\n      This does not issue a ``COUNT()`` query. Instead, the result-set\n      is loaded as it would be during normal iteration, and the length\n      is determined from the size of the result set.\n\n\n.. class:: RawQuery(sql=None, params=None, **kwargs)\n\n   :param str sql: SQL query.\n   :param tuple params: Parameters (optional).\n\n   Create a query by directly specifying the SQL to execute.\n\n\n.. class:: Query(where=None, order_by=None, limit=None, offset=None, **kwargs)\n\n   :param where: Representation of WHERE clause.\n   :param tuple order_by: Columns or values to order by.\n   :param int limit: Value of LIMIT clause.\n   :param int offset: Value of OFFSET clause.\n\n   Base-class for queries that support method-chaining APIs.\n\n   .. method:: with_cte(*cte_list)\n\n      :param cte_list: zero or more :class:`CTE` objects.\n\n      Include the given common-table expressions in the query. Any previously\n      specified CTEs will be overwritten. For examples of common-table\n      expressions, see :ref:`cte`.\n\n   .. method:: cte(name, recursive=False, columns=None)\n\n      :param str name: Alias for common table expression.\n      :param bool recursive: Will this be a recursive CTE?\n      :param list columns: List of column names (as strings).\n\n      Indicate that a query will be used as a common table expression. For\n      example, if we are modelling a category tree and are using a\n      parent-link foreign key, we can retrieve all categories and their\n      absolute depths using a recursive CTE:\n\n      .. code-block:: python\n\n         class Category(Model):\n             name = TextField()\n             parent = ForeignKeyField('self', backref='children', null=True)\n\n         # The base case of our recursive CTE will be categories that are at\n         # the root level -- in other words, categories without parents.\n         roots = (Category\n                  .select(Category.name, Value(0).alias('level'))\n                  .where(Category.parent.is_null())\n                  .cte(name='roots', recursive=True))\n\n         # The recursive term will select the category name and increment\n         # the depth, joining on the base term so that the recursive term\n         # consists of all children of the base category.\n         RTerm = Category.alias()\n         recursive = (RTerm\n                      .select(RTerm.name, (roots.c.level + 1).alias('level'))\n                      .join(roots, on=(RTerm.parent == roots.c.id)))\n\n         # Express <base term> UNION ALL <recursive term>.\n         cte = roots.union_all(recursive)\n\n         # Select name and level from the recursive CTE.\n         query = (cte\n                  .select_from(cte.c.name, cte.c.level)\n                  .order_by(cte.c.name))\n\n         for category in query:\n             print(category.name, category.level)\n\n      For more examples of CTEs, see :ref:`cte`.\n\n   .. method:: where(*expressions)\n\n      :param expressions: zero or more expressions to include in the WHERE\n          clause.\n\n      Include the given expressions in the WHERE clause of the query. The\n      expressions will be AND-ed together with any previously-specified\n      WHERE expressions.\n\n      Example selection users where the username is equal to 'somebody':\n\n      .. code-block:: python\n\n         sq = User.select().where(User.username == 'somebody')\n\n      Example selecting tweets made by users who are either editors or\n      administrators:\n\n      .. code-block:: python\n\n         sq = Tweet.select().join(User).where(\n             (User.is_editor == True) |\n             (User.is_admin == True))\n\n      Example of deleting tweets by users who are no longer active:\n\n      .. code-block:: python\n\n         inactive_users = User.select().where(User.active == False)\n         dq = (Tweet\n               .delete()\n               .where(Tweet.user.in_(inactive_users)))\n         dq.execute()  # Return number of tweets deleted.\n\n      :meth:`~Query.where` calls are chainable.  Multiple calls will\n      be \"AND\"-ed together.\n\n   .. method:: orwhere(*expressions)\n\n      :param expressions: zero or more expressions to include in the WHERE\n          clause.\n\n      Include the given expressions in the WHERE clause of the query. This\n      method is the same as the :meth:`Query.where` method, except that\n      the expressions will be OR-ed together with any previously-specified\n      WHERE expressions.\n\n      Example:\n\n      .. code-block:: python\n\n         query = (User\n                  .select()\n                  .where(User.is_admin == True)\n                  .orwhere(User.is_moderator == True))\n\n   .. method:: order_by(*values)\n\n      :param values: zero or more Column-like objects to order by.\n\n      Define the ORDER BY clause. Any previously-specified values will be\n      overwritten.\n\n   .. method:: order_by_extend(*values)\n\n      :param values: zero or more Column-like objects to order by.\n\n      Extend any previously-specified ORDER BY clause with the given values.\n\n   .. method:: limit(value=None)\n\n      :param int value: specify value for LIMIT clause.\n\n   .. method:: offset(value=None)\n\n      :param int value: specify value for OFFSET clause.\n\n   .. method:: paginate(page, paginate_by=20)\n\n      :param int page: Page number of results (starting from 1).\n      :param int paginate_by: Rows-per-page.\n\n      Convenience method for specifying the LIMIT and OFFSET in a more\n      intuitive way.\n\n      This feature is designed with web-site pagination in mind, so the first\n      page starts with ``page=1``.\n\n      Example:\n\n      .. code-block:: python\n\n         @app.route('/users/')\n         def users():\n             query = User.select().order_by(User.username)\n             page = request.args.get('page')\n             if page and page.isdigit():\n                 page = max(1, int(page))\n             else:\n                 page = 1\n\n             # Render the requested page of results, displaying up to\n             # 20 users per page.\n             return render('users.html', users=query.paginate(page, 20))\n\n\n.. class:: SelectQuery()\n\n   Select query helper-class that implements operator-overloads for creating\n   compound queries.\n\n   .. method:: select_from(*columns)\n\n      :param columns: one or more columns to select from the inner query.\n      :return: a new query that wraps the calling query.\n\n      Create a new query that wraps the current (calling) query. For example,\n      suppose you have a simple ``UNION`` query, and need to apply an\n      aggregation on the union result-set. To do this, you need to write\n      something like:\n\n      .. code-block:: sql\n\n         SELECT \"u\".\"owner\", COUNT(\"u\".\"id\") AS \"ct\"\n         FROM (\n             SELECT \"id\", \"owner\", ... FROM \"cars\"\n             UNION\n             SELECT \"id\", \"owner\", ... FROM \"motorcycles\"\n             UNION\n             SELECT \"id\", \"owner\", ... FROM \"boats\") AS \"u\"\n         GROUP BY \"u\".\"owner\"\n\n      The :meth:`~SelectQuery.select_from` method is designed to simplify\n      constructing this type of query.\n\n      Example peewee code:\n\n      .. code-block:: python\n\n         class Car(Model):\n             owner = ForeignKeyField(Owner, backref='cars')\n             # ... car-specific fields, etc ...\n\n         class Motorcycle(Model):\n             owner = ForeignKeyField(Owner, backref='motorcycles')\n             # ... motorcycle-specific fields, etc ...\n\n         class Boat(Model):\n             owner = ForeignKeyField(Owner, backref='boats')\n             # ... boat-specific fields, etc ...\n\n         cars = Car.select(Car.owner)\n         motorcycles = Motorcycle.select(Motorcycle.owner)\n         boats = Boat.select(Boat.owner)\n\n         union = cars | motorcycles | boats\n\n         query = (union\n                  .select_from(union.c.owner, fn.COUNT(union.c.id))\n                  .group_by(union.c.owner))\n\n   .. method:: union_all(dest)\n\n      Create a UNION ALL query with ``dest``.\n\n   .. method:: __add__(dest)\n\n      Create a UNION ALL query with ``dest``.\n\n   .. method:: union(dest)\n\n      Create a UNION query with ``dest``.\n\n   .. method:: __or__(dest)\n\n      Create a UNION query with ``dest``.\n\n   .. method:: intersect(dest)\n\n      Create an INTERSECT query with ``dest``.\n\n   .. method:: __and__(dest)\n\n      Create an INTERSECT query with ``dest``.\n\n   .. method:: except_(dest)\n\n      Create an EXCEPT query with ``dest``. Note that the method name has a\n      trailing \"_\" character since ``except`` is a Python reserved word.\n\n   .. method:: __sub__(dest)\n\n      Create an EXCEPT query with ``dest``.\n\n\n.. class:: SelectBase()\n\n   Base-class for :class:`Select` and :class:`CompoundSelect` queries.\n\n   .. method:: peek(database, n=1)\n\n      :param Database database: database to execute query against.\n      :param int n: Number of rows to return.\n      :return: A single row if n = 1, else a list of rows.\n\n      Execute the query and return the given number of rows from the start\n      of the cursor. This function may be called multiple times safely, and\n      will always return the first N rows of results.\n\n   .. method:: first(database, n=1)\n\n      :param Database database: database to execute query against.\n      :param int n: Number of rows to return.\n      :return: A single row if n = 1, else a list of rows.\n\n      Like the :meth:`~SelectBase.peek` method, except a ``LIMIT`` is\n      applied to the query to ensure that only ``n`` rows are returned.\n      Multiple calls for the same value of ``n`` will not result in multiple\n      executions.\n\n      The query is altered in-place so it is not possible to call\n      :meth:`~SelectBase.first` and then later iterate over the full\n      result-set using the same query object. Again, this is done to ensure\n      that multiple calls to ``first()`` will not result in multiple query\n      executions.\n\n   .. method:: scalar(database, as_tuple=False, as_dict=False)\n\n      :param Database database: database to execute query against.\n      :param bool as_tuple: Return the result as a tuple?\n      :param bool as_dict: Return the result as a dict?\n      :return: Single scalar value. If ``as_tuple = True``, a row tuple is\n          returned. If ``as_dict = True``, a row dict is returned.\n\n      Return a scalar value from the first row of results. If multiple\n      scalar values are anticipated (e.g. multiple aggregations in a single\n      query) then you may specify ``as_tuple=True`` to get the row tuple.\n\n      Example:\n\n      .. code-block:: python\n\n         query = Note.select(fn.MAX(Note.timestamp))\n         max_ts = query.scalar(db)\n\n         query = Note.select(fn.MAX(Note.timestamp), fn.COUNT(Note.id))\n         max_ts, n_notes = query.scalar(db, as_tuple=True)\n\n         query = Note.select(fn.COUNT(Note.id).alias('count'))\n         assert query.scalar(db, as_dict=True) == {'count': 123}\n\n   .. method:: count(database, clear_limit=False)\n\n      :param Database database: database to execute query against.\n      :param bool clear_limit: Clear any LIMIT clause when counting.\n      :return: Number of rows in the query result-set.\n\n      Return number of rows in the query result-set.\n\n      Implemented by running SELECT COUNT(1) FROM (<current query>).\n\n   .. method:: exists(database)\n\n      :param Database database: database to execute query against.\n      :return: Whether any results exist for the current query.\n\n      Return a boolean indicating whether the current query has any results.\n\n   .. method:: get(database)\n\n      :param Database database: database to execute query against.\n      :return: A single row from the database or ``None``.\n\n      Execute the query and return the first row, if it exists. Multiple\n      calls will result in multiple queries being executed.\n\n      If no matching row is found this method returns ``None`` - this differs\n      from the behavior of :meth:`ModelSelect.get` and :meth:`Model.get`, which\n      raise ``DoesNotExist`` when no row is found.\n\n\n.. class:: CompoundSelectQuery(lhs, op, rhs)\n\n   :param SelectBase lhs: A Select or CompoundSelect query.\n   :param str op: Operation (e.g. UNION, INTERSECT, EXCEPT).\n   :param SelectBase rhs: A Select or CompoundSelect query.\n\n   Class representing a compound SELECT query.\n\n\n.. class:: Select(from_list=None, columns=None, group_by=None, having=None, distinct=None, windows=None, for_update=None, **kwargs)\n\n   :param list from_list: List of sources for FROM clause.\n   :param list columns: Columns or values to select.\n   :param list group_by: List of columns or values to group by.\n   :param Expression having: Expression for HAVING clause.\n   :param distinct: Either a boolean or a list of column-like objects.\n   :param list windows: List of :class:`Window` clauses.\n   :param ForUpdate for_update: indicate SELECT...FOR UPDATE.\n\n   Class representing a SELECT query.\n\n   Rather than instantiating this directly, most-commonly you will use a\n   factory method like :meth:`Table.select` or :meth:`Model.select`.\n\n   Methods on the select query can be chained together.\n\n   Example selecting some user instances from the database.  Only the ``id``\n   and ``username`` columns are selected.  When iterated, will return instances\n   of the ``User`` model:\n\n   .. code-block:: python\n\n      query = User.select(User.id, User.username)\n      for user in query:\n          print(user.username)\n\n   Example selecting users and additionally the number of tweets made by the\n   user.  The ``User`` instances returned will have an additional attribute,\n   'count', that corresponds to the number of tweets made:\n\n   .. code-block:: python\n\n      query = (User\n               .select(User, fn.COUNT(Tweet.id).alias('count'))\n               .join(Tweet, JOIN.LEFT_OUTER)\n               .group_by(User))\n      for user in query:\n          print(user.username, 'has tweeted', user.count, 'times')\n\n   While it is possible to instantiate :class:`Select` directly, more\n   commonly you will build the query using the method-chaining APIs.\n\n   .. method:: columns(*columns)\n\n      :param columns: Zero or more column-like objects to SELECT.\n\n      Specify which columns or column-like values to SELECT.\n\n   .. method:: select(*columns)\n\n      :param columns: Zero or more column-like objects to SELECT.\n\n      Same as :meth:`Select.columns`, provided for\n      backwards-compatibility.\n\n   .. method:: select_extend(*columns)\n\n      :param columns: Zero or more column-like objects to SELECT.\n\n      Extend the current selection with the given columns.\n\n      Example:\n\n      .. code-block:: python\n\n         def get_users(with_count=False):\n             query = User.select()\n             if with_count:\n                 query = (query\n                          .select_extend(fn.COUNT(Tweet.id).alias('count'))\n                          .join(Tweet, JOIN.LEFT_OUTER)\n                          .group_by(User))\n             return query\n\n   .. method:: from_(*sources)\n\n      :param sources: Zero or more sources for the FROM clause.\n\n      Specify which table-like objects should be used in the FROM clause.\n\n      .. code-block:: python\n\n         User = Table('users')\n         Tweet = Table('tweets')\n         query = (User\n                  .select(User.c.username, Tweet.c.content)\n                  .from_(User, Tweet)\n                  .where(User.c.id == Tweet.c.user_id))\n         for row in query.execute(db):\n             print(row['username'], '->', row['content'])\n\n   .. method:: join(dest, join_type='INNER', on=None)\n\n      :param dest: A table or table-like object.\n      :param str join_type: Type of JOIN, default is \"INNER\".\n      :param Expression on: Join predicate.\n\n      Join type may be one of:\n\n      * ``JOIN.INNER``\n      * ``JOIN.LEFT_OUTER``\n      * ``JOIN.RIGHT_OUTER``\n      * ``JOIN.FULL``\n      * ``JOIN.FULL_OUTER``\n      * ``JOIN.CROSS``\n\n      Express a JOIN:\n\n      .. code-block:: python\n\n         User = Table('users', ('id', 'username'))\n         Note = Table('notes', ('id', 'user_id', 'content'))\n\n         query = (Note\n                  .select(Note.content, User.username)\n                  .join(User, on=(Note.user_id == User.id)))\n\n   .. method:: group_by(*columns)\n\n      :param values: zero or more Column-like objects to group by.\n\n      Define the GROUP BY clause. Any previously-specified values will be\n      overwritten.\n\n      Additionally, to specify all columns on a given table, you can pass the\n      table/model object in place of the individual columns.\n\n      Example:\n\n      .. code-block:: python\n\n         query = (User\n                  .select(User, fn.Count(Tweet.id).alias('count'))\n                  .join(Tweet)\n                  .group_by(User))\n\n   .. method:: group_by_extend(*columns)\n\n      :param values: zero or more Column-like objects to group by.\n\n      Extend the GROUP BY clause with the given columns.\n\n   .. method:: having(*expressions)\n\n      :param expressions: zero or more expressions to include in the HAVING\n          clause.\n\n      Include the given expressions in the HAVING clause of the query. The\n      expressions will be AND-ed together with any previously-specified\n      HAVING expressions.\n\n      Example:\n\n      .. code-block:: python\n\n         # Find users with 100 or more Tweets.\n         query = (User\n                  .select(User, fn.Count(Tweet.id).alias('count'))\n                  .join(Tweet)\n                  .group_by(User)\n                  .having(fn.Count(Tweet.id) >= 100))\n\n   .. method:: distinct(*columns)\n\n      :param columns: Zero or more column-like objects.\n\n      Indicate whether this query should use a DISTINCT clause. By specifying\n      a single value of ``True`` the query will use a simple SELECT DISTINCT.\n      Specifying one or more columns will result in a SELECT DISTINCT ON.\n\n   .. method:: window(*windows)\n\n      :param windows: zero or more :class:`Window` objects.\n\n      Define the WINDOW clause. Any previously-specified values will be\n      overwritten.\n\n      Example:\n\n      .. code-block:: python\n\n         # Equivalent example Using a Window() instance instead.\n         window = Window(partition_by=[Sample.counter])\n         query = (Sample\n                  .select(\n                     Sample.counter,\n                     Sample.value,\n                     fn.AVG(Sample.value).over(window))\n                  .window(window)  # Note call to \".window()\"\n                  .order_by(Sample.counter))\n\n   .. method:: for_update(for_update=True, of=None, nowait=None, skip_locked=None)\n\n      :param for_update: Either a boolean or a string indicating the\n          desired lock strength, e.g. \"FOR SHARE\" or \"FOR NO KEY UPDATE\".\n      :param of: One or more models to restrict locking to.\n      :param bool nowait: Specify NOWAIT option when locking.\n      :param bool skip_locked: Specify SKIP LOCKED option when locking.\n\n\n.. class:: _WriteQuery(table, returning=None, **kwargs)\n\n   :param Table table: Table to write to.\n   :param list returning: List of columns for RETURNING clause.\n\n   Base-class for write queries.\n\n   .. method:: returning(*returning)\n\n      :param returning: Zero or more column-like objects for RETURNING clause\n\n      Specify the RETURNING clause of query (Postgresql and Sqlite):\n\n      .. code-block:: python\n\n         query = (User\n                  .insert_many([{'username': 'foo'},\n                                {'username': 'bar'},\n                                {'username': 'baz'}])\n                  .returning(User.id, User.username)\n                  .namedtuples())\n         data = query.execute()\n         for row in data:\n             print('added:', row.username, 'with id=', row.id)\n\n      .. seealso:: :ref:`returning-clause` for additional discussion.\n\n\n.. class:: Update(table, update=None, **kwargs)\n\n   :param Table table: Table to update.\n   :param dict update: Data to update.\n\n   Class representing an UPDATE query.\n\n   See :ref:`updating-records` for additional discussion.\n\n   Example:\n\n   .. code-block:: python\n\n      PageView = Table('page_views')\n      query = (PageView\n               .update({PageView.c.page_views: PageView.c.page_views + 1})\n               .where(PageView.c.url == url))\n      query.execute(database)\n\n   .. method:: from_(*sources)\n\n      :param Source sources: one or more :class:`Table`,\n          :class:`Model`, query, or :class:`ValuesList` to join with.\n\n      Specify additional tables to join with using the UPDATE ... FROM\n      syntax, which is supported by Postgres. The `Postgres documentation <https://www.postgresql.org/docs/10/static/sql-update.html#id-1.9.3.176.8>`_\n      provides additional detail, but to summarize:\n\n         When a ``FROM`` clause is present, what essentially happens is that\n         the target table is joined to the tables mentioned in the\n         from_list, and each output row of the join represents an update\n         operation for the target table. When using ``FROM`` you should\n         ensure that the join produces at most one output row for each row\n         to be modified.\n\n      Example:\n\n      .. code-block:: python\n\n         # Update multiple users in a single query.\n         data = [('huey', True),\n                 ('mickey', False),\n                 ('zaizee', True)]\n         vl = ValuesList(data, columns=('username', 'is_admin'), alias='vl')\n\n         # Here we'll update the \"is_admin\" status of the above users,\n         # \"joining\" the VALUES() on the \"username\" column.\n         query = (User\n                  .update(is_admin=vl.c.is_admin)\n                  .from_(vl)\n                  .where(User.username == vl.c.username))\n\n      The above query produces the following SQL:\n\n      .. code-block:: sql\n\n         UPDATE \"users\" SET \"is_admin\" = \"vl\".\"is_admin\"\n         FROM (\n             VALUES ('huey', t), ('mickey', f), ('zaizee', t))\n             AS \"vl\"(\"username\", \"is_admin\")\n         WHERE (\"users\".\"username\" = \"vl\".\"username\")\n\n\n.. class:: Insert(table, insert=None, columns=None, on_conflict=None, **kwargs)\n\n   :param Table table: Table to INSERT data into.\n   :param insert: Either a dict, a list, or a query.\n   :param list columns: List of columns when ``insert`` is a list or query.\n   :param on_conflict: Conflict resolution strategy.\n\n   Class representing an INSERT query.\n\n   See :ref:`inserting-records` for additional discussion.\n\n   Example:\n\n   .. code-block:: python\n\n      User = Table('users')\n\n      query = User.insert({User.c.username: 'huey'})\n      query.execute(database)\n\n   .. method:: as_rowcount(as_rowcount=True)\n\n      :param bool as_rowcount: Whether to return the modified row count (as\n          opposed to the last-inserted row id).\n\n      SQLite and MySQL return the last inserted rowid. Postgresql will return a\n      cursor for iterating over the inserted id(s).\n\n      If you prefer to receive the inserted row-count, then specify\n      ``as_rowcount()``:\n\n      .. code-block:: python\n\n         db = MySQLDatabase(...)\n\n         query = User.insert_many([...])\n         # By default, the last rowid is returned:\n         #last_id = query.execute()\n\n         # To get the modified row-count:\n         rowcount = query.as_rowcount().execute()\n\n   .. method:: on_conflict_ignore(ignore=True)\n\n      :param bool ignore: Whether to add ON CONFLICT IGNORE clause.\n\n      Specify IGNORE conflict resolution strategy.\n\n   .. method:: on_conflict_replace(replace=True)\n\n      :param bool replace: Whether to add ON CONFLICT REPLACE clause.\n\n      Specify REPLACE conflict resolution strategy (SQLite and MySQL only).\n\n   .. method:: on_conflict(action=None, update=None, preserve=None, where=None, conflict_target=None, conflict_where=None, conflict_constraint=None)\n\n      :param str action: Action to take when resolving conflict. If blank,\n          action is assumed to be \"update\".\n      :param update: A dictionary mapping column to new value.\n      :param preserve: A list of columns whose values should be preserved from the original INSERT.\n      :param where: Expression to restrict the conflict resolution.\n      :param conflict_target: Column(s) that comprise the constraint.\n      :param conflict_where: Expressions needed to match the constraint target if it is a partial index (index with a WHERE clause).\n      :param str conflict_constraint: Name of constraint to use for conflict\n          resolution. Currently only supported by Postgres.\n\n      Specify the parameters for an :class:`OnConflict` clause to use for\n      conflict resolution.\n\n      See :ref:`upsert` for additional discussion.\n\n      Examples:\n\n      .. code-block:: python\n\n         class User(Model):\n             username = TextField(unique=True)\n             last_login = DateTimeField(null=True)\n             login_count = IntegerField()\n\n         def log_user_in(username):\n             now = datetime.datetime.now()\n\n             # INSERT a new row for the user with the current timestamp and\n             # login count set to 1. If the user already exists, then we\n             # will preserve the last_login value from the \"insert()\" clause\n             # and atomically increment the login-count.\n             userid = (User\n                       .insert(username=username, last_login=now, login_count=1)\n                       .on_conflict(\n                           conflict_target=[User.username],\n                           preserve=[User.last_login],\n                           update={User.login_count: User.login_count + 1})\n                       .execute())\n             return userid\n\n      Example using the special :class:`EXCLUDED` namespace:\n\n      .. code-block:: python\n\n         class KV(Model):\n             key = CharField(unique=True)\n             value = IntegerField()\n\n         # Create one row.\n         KV.create(key='k1', value=1)\n\n         # Demonstrate usage of EXCLUDED.\n         # Here we will attempt to insert a new value for a given key. If that\n         # key already exists, then we will update its value with the *sum* of its\n         # original value and the value we attempted to insert -- provided that\n         # the new value is larger than the original value.\n         query = (KV.insert(key='k1', value=10)\n                  .on_conflict(conflict_target=[KV.key],\n                               update={KV.value: KV.value + EXCLUDED.value},\n                               where=(EXCLUDED.value > KV.value)))\n\n         # Executing the above query will result in the following data being\n         # present in the \"kv\" table:\n         # (key='k1', value=11)\n         query.execute()\n\n         # If we attempted to execute the query *again*, then nothing would be\n         # updated, as the new value (10) is now less than the value in the\n         # original row (11).\n\n\n.. class:: Delete()\n\n   Class representing a DELETE query.\n\n   See :ref:`deleting-records` for additional discussion.\n\n   Example:\n\n   .. code-block:: python\n\n      Tweet = Table('tweets')\n\n      # Delete all unpublished tweets older than 30 days.\n      cutoff = datetime.datetime.now() - datetime.timedelta(days=30)\n      query = (Tweet\n               .delete()\n               .where(\n                   (Tweet.c.is_published == False) &\n                   (Tweet.c.timestamp < cutoff)))\n      nrows = query.execute(database)\n\n\n.. function:: prefetch(sq, *subqueries, prefetch_type=PREFETCH_TYPE.WHERE)\n\n   :param sq: Query to use as starting-point.\n   :param subqueries: One or more models or :class:`ModelSelect` queries\n       to eagerly fetch.\n   :param prefetch_type: Query type to use for the subqueries.\n   :return: a list of models with selected relations prefetched.\n\n   Eagerly fetch related objects, allowing efficient querying of multiple\n   tables when a 1-to-many relationship exists. The prefetch type changes how\n   the subqueries are constructed which may be desirable depending on the\n   database engine in use.\n\n   Prefetch type may be one of:\n\n   * ``PREFETCH_TYPE.WHERE``\n   * ``PREFETCH_TYPE.JOIN``\n\n   See :ref:`relationships` for in-depth discussion of joining and prefetch.\n\n   For example, it is simple to query a many-to-1 relationship efficiently:\n\n   .. code-block:: python\n\n      query = (Tweet\n               .select(Tweet, User)\n               .join(User))\n      for tweet in query:\n          # Looking up tweet.user.username does not require a query since\n          # the related user's columns were selected.\n          print(tweet.user.username, '->', tweet.content)\n\n   To efficiently do the inverse, query users and their tweets, you can use\n   prefetch:\n\n   .. code-block:: python\n\n      query = User.select()\n      for user in prefetch(query, Tweet):\n          print(user.username)\n          for tweet in user.tweets:  # Does not require additional query.\n              print('    ', tweet.content)\n\n   Because ``prefetch`` must reconstruct a graph of models, it is\n   necessary to be sure that the foreign-key/primary-key of any\n   related models are selected, so that the related objects can be\n   mapped correctly.\n\n\nQuery-builder Internals\n-----------------------\n\n.. class:: AliasManager()\n\n   Manages the aliases assigned to :class:`Source` objects in SELECT\n   queries, so as to avoid ambiguous references when multiple sources are\n   used in a single query.\n\n   .. method:: add(source)\n\n      Add a source to the AliasManager's internal registry at the current\n      scope. The alias will be automatically generated using the following\n      scheme (where each level of indentation refers to a new scope):\n\n      :param Source source: Make the manager aware of a new source. If the\n          source has already been added, the call is a no-op.\n\n   .. method:: get(source, any_depth=False)\n\n      Return the alias for the source in the current scope. If the source\n      does not have an alias, it will be given the next available alias.\n\n      :param Source source: The source whose alias should be retrieved.\n      :return: The alias already assigned to the source, or the next\n          available alias.\n      :rtype: str\n\n   .. method:: __setitem__(source, alias)\n\n      Manually set the alias for the source at the current scope.\n\n      :param Source source: The source for which we set the alias.\n\n   .. method:: push()\n\n      Push a new scope onto the stack.\n\n   .. method:: pop()\n\n      Pop scope from the stack.\n\n\n.. class:: State(scope, parentheses=False, subquery=False, **kwargs)\n\n   Lightweight object for representing the state at a given scope. During SQL\n   generation, each object visited by the :class:`Context` can inspect the\n   state. The :class:`State` class allows Peewee to do things like:\n\n   * Use a common interface for field types or SQL expressions, but use\n     vendor-specific data-types or operators.\n   * Compile a :class:`Column` instance into a fully-qualified attribute,\n     as a named alias, etc, depending on the value of the ``scope``.\n   * Ensure parentheses are used appropriately.\n\n   :param int scope: The scope rules to be applied while the state is active.\n   :param bool parentheses: Wrap the contained SQL in parentheses.\n   :param bool subquery: Whether the current state is a child of an outer\n       query.\n   :param dict kwargs: Arbitrary settings which should be applied in the\n       current state.\n\n\n.. class:: Context(**settings)\n\n   Converts Peewee structures into parameterized SQL queries.\n\n   Peewee structures should all implement a `__sql__` method, which will be\n   called by the `Context` class during SQL generation. The `__sql__` method\n   accepts a single parameter, the `Context` instance, which allows for\n   recursive descent and introspection of scope and state.\n\n   .. attribute:: scope\n\n      Return the currently-active scope rules.\n\n   .. attribute:: parentheses\n\n      Return whether the current state is wrapped in parentheses.\n\n   .. attribute:: subquery\n\n      Return whether the current state is the child of another query.\n\n   .. method:: scope_normal(**kwargs)\n\n      The default scope. Sources are referred to by alias, columns by\n      dotted-path from the source.\n\n   .. method:: scope_source(**kwargs)\n\n      Scope used when defining sources, e.g. in the column list and FROM\n      clause of a SELECT query. This scope is used for defining the\n      fully-qualified name of the source and assigning an alias.\n\n   .. method:: scope_values(**kwargs)\n\n      Scope used for UPDATE, INSERT or DELETE queries, where instead of\n      referencing a source by an alias, we refer to it directly. Similarly,\n      since there is a single table, columns do not need to be referenced\n      by dotted-path.\n\n   .. method:: scope_cte(**kwargs)\n\n      Scope used when generating the contents of a common-table-expression.\n      Used after a WITH statement, when generating the definition for a CTE\n      (as opposed to merely a reference to one).\n\n   .. method:: scope_column(**kwargs)\n\n      Scope used when generating SQL for a column. Ensures that the column is\n      rendered with its correct alias. Was needed because when referencing\n      the inner projection of a sub-select, Peewee would render the full\n      SELECT query as the \"source\" of the column (instead of the query's\n      alias + . + column).  This scope allows us to avoid rendering the full\n      query when we only need the alias.\n\n   .. method:: sql(obj)\n\n      Append a composable Node object, sub-context, or other object to the\n      query AST. Python values, such as integers, strings, floats, etc. are\n      treated as parameterized values.\n\n      :return: The updated Context object.\n\n   .. method:: literal(keyword)\n\n      Append a string-literal to the current query AST.\n\n      :return: The updated Context object.\n\n   .. method:: parse(node)\n\n      :param Node node: Instance of a Node subclass.\n      :return: a 2-tuple consisting of (sql, parameters).\n\n      Convert the given node to a SQL AST and return a 2-tuple consisting\n      of the SQL query and the parameters.\n\n   .. method:: query()\n\n      :return: a 2-tuple consisting of (sql, parameters) for the context.\n\n\nConstants and Helpers\n---------------------\n\n.. class:: Proxy()\n\n   Create a proxy or placeholder for another object.\n\n   .. method:: initialize(obj)\n\n      :param obj: Object to proxy to.\n\n      Bind the proxy to the given object. Afterwards all attribute lookups\n      and method calls on the proxy will be sent to the given object.\n\n      Any callbacks that have been registered will be called.\n\n   .. method:: attach_callback(callback)\n\n      :param callback: A function that accepts a single parameter, the bound\n          object.\n      :return: self\n\n      Add a callback to be executed when the proxy is initialized.\n\n.. class:: DatabaseProxy()\n\n   :class:`Proxy` subclass that is suitable to use as a placeholder for a\n   :class:`Database` instance.\n\n   See :ref:`initializing-database` for details.\n\n   Example:\n\n   .. code-block:: python\n\n      db = DatabaseProxy()\n\n      class BaseModel(Model):\n          class Meta:\n              database = db\n\n      # ... some time later ...\n      if app.config['DEBUG']:\n          database = SqliteDatabase('local.db')\n      elif app.config['TESTING']:\n          database = SqliteDatabase(':memory:')\n      else:\n          database = PostgresqlDatabase('production')\n\n      db.initialize(database)\n\n.. function:: chunked(iterable, n)\n\n   :param iterable: an iterable that is the source of the data to be chunked.\n   :param int n: chunk size\n   :return: a new iterable that yields *n*-length chunks of the source data.\n\n   Efficient implementation for breaking up large lists of data into\n   smaller-sized chunks.\n\n   Usage:\n\n   .. code-block:: python\n\n      it = range(10)  # An iterable that yields 0...9.\n\n      # Break the iterable into chunks of length 4.\n      for chunk in chunked(it, 4):\n          print(', '.join(str(num) for num in chunk))\n\n      # PRINTS:\n      # 0, 1, 2, 3\n      # 4, 5, 6, 7\n      # 8, 9\n\n\n.. class:: IndexMetadata\n\n   Metadata for indexes returned by :meth:`Database.get_indexes`\n\n   .. data:: name\n             sql\n             columns\n             unique\n             table\n\n\n.. class:: ColumnMetadata\n\n   Metadata for columns returned by :meth:`Database.get_columns`\n\n   .. data:: name\n             data_type\n             null\n             primary_key\n             table\n             default\n\n\n.. class:: ForeignKeyMetadata\n\n   Metadata for foreign keys returned by :meth:`Database.get_foreign_keys`\n\n   .. data:: column\n             dest_table\n             dest_column\n             table\n\n\n.. class:: ViewMetadata\n\n   Metadata for views returned by :meth:`Database.get_views`\n\n   .. data:: name\n             sql\n\n\nPlayhouse Reference\n-------------------\n\n+---------------------------------------+---------------------------+\n| Module                                | Section                   |\n+=======================================+===========================+\n| playhouse.sqlite_ext                  | :ref:`sqlite`             |\n+---------------------------------------+---------------------------+\n| playhouse.cysqlite_ext                | :ref:`cysqlite-ext`       |\n+---------------------------------------+---------------------------+\n| playhouse.sqliteq                     | :ref:`sqliteq`            |\n+---------------------------------------+---------------------------+\n| playhouse.sqlite_udf                  | :ref:`sqlite-udf`         |\n+---------------------------------------+---------------------------+\n| playhouse.apsw_ext                    | :ref:`apsw`               |\n+---------------------------------------+---------------------------+\n| playhouse.sqlcipher_ext               | :ref:`sqlcipher`          |\n+---------------------------------------+---------------------------+\n| playhouse.postgres_ext                | :ref:`postgresql`         |\n+---------------------------------------+---------------------------+\n| playhouse.cockroachdb                 | :ref:`crdb`               |\n+---------------------------------------+---------------------------+\n| playhouse.mysql_ext                   | :ref:`mysql`              |\n+---------------------------------------+---------------------------+\n| playhouse.db_url                      | :ref:`db-url`             |\n+---------------------------------------+---------------------------+\n| playhouse.pool                        | :ref:`pool`               |\n+---------------------------------------+---------------------------+\n| playhouse.migrate                     | :ref:`migrate`            |\n+---------------------------------------+---------------------------+\n| playhouse.reflection                  | :ref:`reflection`         |\n+---------------------------------------+---------------------------+\n| playhouse.test_utils                  | :ref:`test-utils`         |\n+---------------------------------------+---------------------------+\n| playhouse.fields                      | :ref:`extra-fields`       |\n+---------------------------------------+---------------------------+\n| playhouse.shortcuts                   | :ref:`shortcuts`          |\n+---------------------------------------+---------------------------+\n| playhouse.hybrid                      | :ref:`hybrid`             |\n+---------------------------------------+---------------------------+\n| playhouse.kv                          | :ref:`kv`                 |\n+---------------------------------------+---------------------------+\n| playhouse.signals                     | :ref:`signals`            |\n+---------------------------------------+---------------------------+\n| playhouse.dataset                     | :ref:`dataset`            |\n+---------------------------------------+---------------------------+\n| playhouse.flask_utils                 | :ref:`flask-utils`        |\n+---------------------------------------+---------------------------+\n"
  },
  {
    "path": "docs/peewee/asyncio.rst",
    "content": ".. _pwasyncio:\n\nAsync Support\n=============\n\n.. module:: playhouse.pwasyncio\n\nPeewee's async extension bridges blocking query execution to the asyncio event\nloop using ``greenlet``. When database I/O occurs inside a greenlet, control is\ntransparently yielded to the event loop until the driver completes the\noperation. This allows synchronous Peewee code to run unmodified within an\nasync context.\n\nExample\n-------\n\n``playhouse.pwasyncio`` contains the async database implementations. Typically\nthis is the only thing you will need in order to use Peewee with asyncio:\n\n.. code-block:: python\n\n   import asyncio\n   from peewee import *\n   from playhouse.pwasyncio import AsyncSqliteDatabase\n\n   db = AsyncSqliteDatabase('my_app.db')\n\n   class User(db.Model):\n       name = TextField()\n\nQueries must be executed through an async execution method. This ensures that\nwhen blocking would occur, control is properly yielded to the event loop. The\ndatabase context (``async with db``) acquires a connection from the pool and\nreleases it on exit:\n\n.. code-block:: python\n\n   async def main():\n       async with db:\n           await db.acreate_tables([User])\n\n           # Create a new user in a transaction.\n           async with db.atomic() as txn:\n               user = await db.run(User.create, name='Charlie')\n\n           # Fetch a single row from the database.\n           charlie = await db.get(User.select().where(User.name == 'Charlie'))\n           assert charlie.name == user.name\n\n           # Execute a query and iterate results.\n           for user in await db.list(User.select().order_by(User.name)):\n               print(user.name)\n\n           # Async lazy result fetching (uses server-side cursors where\n           # available).\n           query = User.select().order_by(User.name)\n           async for user in db.iterate(query):\n               print(user.name)\n\n       await db.close_pool()\n\n   asyncio.run(main())\n\nInstallation\n------------\n\nRequires Python 3.8 or newer, ``greenlet`` and an async database driver:\n\n.. code-block:: shell\n\n   pip install peewee greenlet\n\n   pip install aiosqlite  # SQLite\n   pip install asyncpg  # Postgresql\n   pip install aiomysql  # MySQL / MariaDB\n\nSupported backends:\n\n================  ============  ====================================\nDatabase          Driver        Peewee class\n================  ============  ====================================\nSQLite            aiosqlite     :class:`AsyncSqliteDatabase`\nPostgresql        asyncpg       :class:`AsyncPostgresqlDatabase`\nMySQL / MariaDB   aiomysql      :class:`AsyncMySQLDatabase`\n================  ============  ====================================\n\nExecution Methods\n-----------------\n\n``db.run()`` - general-purpose entry point\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`~AsyncDatabaseMixin.run` accepts any callable and runs it inside a\ngreenlet bridge. The callable can contain arbitrary synchronous Peewee code,\nincluding transactions:\n\n.. code-block:: python\n\n   # Single operation:\n   user = await db.run(User.create, name='Alice')\n\n   # Multi-step function:\n   def register(username, bio):\n       with db.atomic():\n           user = User.create(name=username)\n           Profile.create(user=user, bio=bio)\n           return user\n\n   user = await db.run(register, 'alice', 'Python developer')\n\nUse ``db.run()`` when:\n\n* You have existing synchronous code you want to call from async.\n* A single operation involves multiple queries (e.g. a transaction).\n\nAsync Helper Methods\n^^^^^^^^^^^^^^^^^^^^^\n\nFor single-query operations, the async helpers are more direct:\n\n.. code-block:: python\n\n   # Execute any query and get its natural return type.\n   cursor = await db.aexecute(query)\n\n   # Use a transaction:\n   async with db.atomic() as tx:\n       await db.run(User.create, name='Bob')\n\n   # SELECT and return one model instance (raises DoesNotExist if none).\n   user = await db.get(User.select().where(User.name == 'Alice'))\n\n   # SELECT and return a list.\n   users = await db.list(User.select().order_by(User.name))\n\n   # SELECT and stream results from the database asynchronously.\n   users = [user async for user in db.iterate(User.select())]\n\n   # SELECT and return a scalar value.\n   count = await db.scalar(User.select(fn.COUNT(User.id)))\n\n   # Or user shortcut.\n   count = await db.count(User.select())\n\n   # CREATE TABLE / DROP TABLE:\n   await db.acreate_tables([User, Tweet])\n   await db.adrop_tables([User, Tweet])\n\n   # Raw SQL:\n   cursor = await db.aexecute_sql('SELECT 1')\n   print(cursor.fetchall())   # [(1,)]\n\nTransactions\n^^^^^^^^^^^^^\n\nUse ``async with db.atomic()`` for async-aware transactions:\n\n.. code-block:: python\n\n   async with db.atomic():\n       await db.run(User.create, name='Alice')\n       await db.run(User.create, name='Bob')\n\n       # Nesting and explicit commit/rollback work.\n       async with db.atomic() as nested:\n           await db.aexecute(User.delete().where(User.name == 'Bob'))\n           await nested.arollback()  # Un-delete Bob.\n\n   # Both Alice and Bob are in the database.\n\nOr wrap transactional code in ``db.run()``:\n\n.. code-block:: python\n\n   def create_users():\n       with db.atomic():\n           User.create(name='Alice')\n           User.create(name='Bob')\n\n           with db.atomic() as nested:\n               User.delete().where(User.name == 'Bob').execute()\n               nested.rollback()  # Un-delete Bob.\n\n   await db.run(create_users)\n\n   # Both Alice and Bob are in the database.\n\nBoth approaches produce the same result. The ``db.run()`` form is often simpler\nwhen the transactional logic involves many inter-dependent queries.\n\nConnection Management\n---------------------\n\nThe database context manager (``async with db``) is the recommended way to\nmanage connections. It acquires a connection on entry and releases it on exit:\n\n.. code-block:: python\n\n   async with db:\n       # Connection is available here.\n       pass\n   # Connection released.\n\nExplicit control is also available:\n\n.. code-block:: python\n\n   await db.aconnect()    # Acquire connection for the current task.\n   # ... queries ...\n   await db.aclose()      # Release connection back to pool.\n\nEach asyncio task gets its own connection from the pool. **Connections are not\nshared between tasks**. Each async task will have it's own connection and\ntransaction state - this prevents bugs that may occur when connections are\nshared and transactions end up interleaved across several running tasks.\n\nTo shut down completely (e.g. during application teardown):\n\n.. code-block:: python\n\n   await db.close_pool()\n\nMySQL and Postgresql\n^^^^^^^^^^^^^^^^^^^^\n\nMySQL and Postgresql use the driver's native connection pool.\n\nPool configuration options include:\n\n* ``pool_size``: Maximum number of connections\n* ``pool_min_size``: Minimum pool size\n* ``acquire_timeout``: Timeout when acquiring a connection\n\n.. code-block:: python\n\n   db = AsyncPostgresqlDatabase(\n       'peewee_test',\n       host='localhost',\n       user='postgres',\n       pool_size=10,\n       pool_min_size=1,\n       acquire_timeout=10)\n\nSQLite\n^^^^^^\n\nPeewee provides a simple connection-pooling implementation for SQLite\nconnections.\n\nPool configuration options include:\n\n* ``pool_size``: Maximum number of connections\n* ``acquire_timeout``: Timeout when acquiring a connection\n\nSQLite operates on local disk storage, so queries typically execute extremely\nquickly. The cost of dispatching to a background thread and wrapping in\ncoroutines increases the latency per query. For every query executed, a closure\nmust be created, a future allocated, a queue written-to, a loop\n``call_soon_threadsafe()`` issued, and two context switches made. This is the\ncase with `aiosqlite <https://github.com/omnilib/aiosqlite/blob/main/aiosqlite/core.py>`__.\n\nAdditionally, SQLite only allows one writer at a time, so while using an async\nwrapper may keep things responsive while waiting to obtain the write lock,\nwrites will not occur \"faster\", the bottleneck has merely been moved.\nConversely, if you don’t have that much load, the async wrapper adds complexity\nand overhead for no measurable benefit.\n\nTo use SQLite in an async environment anyways, it is strongly recommended to\nuse WAL-mode at a minimum, which allows multiple readers to co-exist with a\nsingle writer:\n\n.. code-block:: python\n\n   db = AsyncSqliteDatabase('app.db', pragmas={'journal_mode': 'wal'})\n\n\nSharp Corners\n-------------\n\nLazy foreign key access outside ``db.run()``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAccessing a lazy foreign key attribute triggers a synchronous query if the\nobject has not been populated. Outside a greenlet context, this raises ``MissingGreenletBridge``:\n\n.. code-block:: python\n\n   tweet = await db.get(Tweet.select())\n\n   # FAILS: triggers a SELECT outside the greenlet bridge.\n   print(tweet.user.name)\n   # MissingGreenletBridge: Attempted query outside greenlet runner.\n\nFix by selecting the related model in the original query:\n\n.. code-block:: python\n\n   query = Tweet.select(Tweet, User).join(User)\n   tweet = await db.get(query)\n   print(tweet.user.name)   # OK - no extra query.\n\nOr by wrapping the access in ``db.run()``:\n\n.. code-block:: python\n\n   name = await db.run(lambda: tweet.user.name)\n\nThe safest approach is to disable lazy-loading on your foreign-key fields and\nenforce selecting relations via explicit joins.\n\n.. code-block:: python\n\n   class Tweet(db.Model):\n       user = ForeignKeyField(User, backref='tweets', lazy_load=False)\n       ...\n\n\nIterating back-references outside ``db.run()``\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIterating a back-reference outside a greenlet context also fails for the same\nreason as above.\n\n.. code-block:: python\n\n   # FAILS:\n   for tweet in user.tweets:\n       print(tweet.content)\n\nSolutions:\n\n.. code-block:: python\n\n   # Using db.list():\n   for tweet in await db.list(user.tweets):\n       print(tweet.content)\n\n   # Using db.run() with list():\n   tweets = await db.run(list, user.tweets)\n\n   # Use prefetch:\n   users = await db.run(\n       prefetch,\n       User.select().where(User.username.in_(('Charlie', 'Huey', 'Mickey')))\n       Tweet.select())\n\n   for user in users:\n       for tweet in user.tweets:  # Prefetched - no extra query.\n           print(tweet.content)\n\nAny code that triggers a database query must execute via either ``db.run()`` or\none of the async helper methods.\n\nAPI Reference\n-------------\n\n.. class:: AsyncDatabaseMixin(database, pool_size=10, pool_min_size=1, acquire_timeout=10, **kwargs)\n\n   :param str database: Database name or filename for SQLite.\n   :param int pool_size: Maximum size of the driver-managed connection pool\n       (no-op for SQLite).\n   :param int pool_min_size: Minimum size of the driver-managed connection pool\n       (no-op for SQLite).\n   :param float acquire_timeout: Time (in seconds) to wait for a free\n       connection when acquiring from the pool.\n   :param kwargs: Arbitrary keyword arguments passed to the underlying database\n       driver when creating connections (e.g., ``user``, ``password``,\n       ``host``).\n\n   Mixin class providing asyncio execution support. Use a driver-specific\n   subclass in application code:\n\n   * :class:`AsyncSqliteDatabase`\n   * :class:`AsyncPostgresqlDatabase`\n   * :class:`AsyncMySQLDatabase`\n\n   Each asyncio task maintains its own connection state and transaction stack.\n   Connections are acquired and released back to the pool when the task\n   completes or the database context exits.\n\n   .. method:: run(fn, *args, **kwargs)\n      :async:\n\n      :param fn: A synchronous callable.\n      :returns: The return value of ``fn(*args, **kwargs)``.\n\n      Execute a synchronous callable inside a greenlet and return the result.\n      This is the primary entry point for executing Peewee ORM code in an\n      async context.\n\n      When database I/O or blocking would occur, control is yielded to the\n      event-loop automatically.\n\n      Example:\n\n      .. code-block:: python\n\n         db = AsyncSqliteDatabase(':memory:')\n\n         class User(db.Model):\n             username = TextField()\n\n         def setup_app():\n             # Ensure table exists and admin user is present at startup.\n             with db:\n                 db.create_tables([User])\n\n                 # Create admin user if does not exist.\n                 try:\n                     with db.atomic():\n                         User.create(username='admin')\n                 except IntegrityError:\n                     pass\n\n         async def main():\n             await db.run(setup_app)\n\n             # We can pass arguments to the synchronous callable and get\n             # return values as well.\n             admin_user = await db.run(User.get, User.username == 'admin')\n\n   .. method:: aconnect()\n      :async:\n\n      :return: A wrapped async connection.\n\n      Acquire a connection from the pool for the current task. Typically the\n      connection is not used directly, since the connection will be bound to\n      the task using a task-local.\n\n      Example:\n\n      .. code-block:: python\n\n         # Acquire a connection from the pool which will be used for the\n         # current asyncio task.\n         await db.aconnect()\n\n         # Run some queries.\n         users = await db.list(User.select().order_by(User.username))\n         for user in users:\n             print(user.username)\n\n         # Close connection, which releases it back to the pool.\n         await db.aclose()\n\n      Typically applications should prefer to use the async context-manager for\n      connection management, e.g.:\n\n      .. code-block:: python\n\n         db = AsyncSqliteDatabase(':memory:')\n\n         async with db:\n             # Connection is obtained from the pool and used for this task.\n             await db.acreate_tables([User, Tweet])\n\n         # Context block exits, connection is released back to pool.\n\n   .. method:: aclose()\n      :async:\n\n      Release the current task's connection back to the pool.\n\n   .. method:: close_pool()\n      :async:\n\n      Close the underlying connection pool and release all active connections.\n\n      This method should be called during application shutdown.\n\n   .. method:: __aenter__()\n               __aexit__(exc_type, exc, tb)\n      :async:\n\n      Async database context, acquiring a connection for the current task for\n      the duration of the wrapped block.\n\n      .. code-block:: python\n\n         db = AsyncSqliteDatabase(':memory:')\n\n         async with db:\n             # Connection is obtained from the pool and used for this task.\n             await db.acreate_tables([User, Tweet])\n\n         # Context block exits, connection is released back to pool.\n\n   .. method:: aexecute(query)\n      :async:\n\n      :param Query query: a Select, Insert, Update or Delete query.\n      :return: the normal return-value for the query type.\n\n      Execute any Peewee query object and return its result.\n\n      Example:\n\n      .. code-block:: python\n\n         insert = User.insert(username='Huey')\n         pk = await db.aexecute(insert)\n\n         update = (Tweet\n                   .update(is_published=True)\n                   .where(Tweet.timestamp <= datetime.now()))\n         nrows = await db.aexecute(update)\n\n         spammers = (User\n                     .delete()\n                     .where(User.username.contains('billing'))\n                     .returning(User.username))\n         for u in await db.aexecute(spammers):\n             print(f'Deleted: {u.username}')\n\n   .. method:: get(query)\n      :async:\n\n      :param Query query: a Select query.\n\n      Execute a SELECT query and return a single model instance.\n      Raises :exc:`~Model.DoesNotExist` if no row matches.\n\n      Example:\n\n      .. code-block:: python\n\n         huey = await db.get(User.select().where(User.username == 'Huey'))\n\n         # Fetch a model and a relation in single query.\n         query = Tweet.select(Tweet, User).join(User).where(Tweet.id == 123)\n         tweet = await db.get(query)\n         print(tweet.user.username, '->', tweet.content)\n\n   .. method:: list(query)\n      :async:\n\n      :param Query query: a Select query, or an Insert, Update or Delete\n          query that utilizes RETURNING.\n\n      Execute a SELECT (or INSERT/UPDATE/DELETE with RETURNING) and return\n      a list of results.\n\n      Example:\n\n      .. code-block:: python\n\n         query = User.select().order_by(User.username)\n         async for user in db.list(query):\n             print(user.username)\n\n   .. method:: iterate(query)\n      :async:\n\n      :param Query query: a Select query to stream results from using an async\n         generator.\n\n      :meth:`~AsyncDatabaseMixin.iterate` method uses server-side cursors\n      (MySQL and Postgres) to efficiently stream large result-sets.\n\n      Example:\n\n      .. code-block:: python\n\n         query = User.select().order_by(User.username)\n         async for user in db.iterate(query):\n             print(user.username)\n\n   .. method:: scalar(query)\n      :async:\n\n      :param Query query: a Select query.\n\n      Execute a SELECT and return the first column of the first row.\n\n      Example:\n\n      .. code-block:: python\n\n         max_id = await db.scalar(User.select(fn.MAX(User.id)))\n\n   .. method:: count(query)\n      :async:\n\n      :param Query query: a Select query.\n\n      Wrap the query in a SELECT COUNT(...) and return the count of rows.\n\n      Example:\n\n      .. code-block:: python\n\n         tweets = await db.count(Tweet.select().where(Tweet.is_published))\n\n   .. method:: exists(query)\n      :async:\n\n      :param Query query: a Select query.\n\n      Return boolean whether the query contains any results.\n\n   .. method:: aprefetch(query, *subqueries)\n      :async:\n\n      :param Query query: Query to use as starting-point.\n      :param subqueries: One or more models or :class:`ModelSelect` queries\n          to eagerly fetch.\n      :return: a list of models with selected relations prefetched.\n\n      Eagerly fetch related objects, allowing efficient querying of multiple\n      tables when a 1-to-many relationship exists.\n\n      .. code-block:: python\n\n         users = User.select().order_by(User.username)\n         tweets = Tweet.select().order_by(Tweet.timestamp)\n\n         for user in await db.aprefetch(users, tweets):\n             print(user.username)\n             for tweet in user.tweets:\n                 print('    ', tweet.content)\n\n   .. method:: atomic()\n\n      Return an async-aware atomic context manager. Supports both\n      ``async with`` and ``with``.\n\n      Example of async usage:\n\n      .. code-block:: python\n\n         async def transfer_funds(src, dest, amount):\n             async with db.atomic() as txn:\n                 await db.aexecute(\n                     Account\n                     .update(balance=Account.balance - amount)\n                     .where(Account.id == src.id))\n\n                 await db.aexecute(\n                     Account\n                     .update(balance=Account.balance + amount)\n                     .where(Account.id == dest.id))\n\n         async def main():\n             await transfer_funds(user1, user2, 100.)\n\n      Example of sync usage:\n\n      .. code-block:: python\n\n         def transfer_funds(src, dest, amount):\n             with db.atomic() as txn:\n                 (Account\n                  .update(balance=Account.balance - amount)\n                  .where(Account.id == src.id)\n                  .execute())\n\n                 (Account\n                  .update(balance=Account.balance + amount)\n                  .where(Account.id == dest.id)\n                  .execute())\n\n         async def main():\n             await db.run(transfer_funds, user1, user2, 100.)\n\n   .. method:: acreate_tables(models, **options)\n      :async:\n\n      :param list models: A list of :class:`Model` classes.\n      :param options: Options to specify when calling\n          :meth:`Model.create_table`.\n\n      Create tables, indexes and associated constraints for the given list of\n      models.\n\n      Dependencies are resolved so that tables are created in the appropriate\n      order.\n\n      Example:\n\n      .. code-block:: python\n\n         class User(db.Model):\n             ...\n\n         class Tweet(db.Model):\n             ...\n\n         async def setup_hook():\n             async with db:\n                 await db.acreate_tables([User, Tweet])\n\n   .. method:: adrop_tables(models, **options)\n      :async:\n\n      :param list models: A list of :class:`Model` classes.\n      :param kwargs: Options to specify when calling\n          :meth:`Model.drop_table`.\n\n      Drop tables, indexes and constraints for the given list of models.\n\n   .. method:: aexecute_sql(sql, params=None)\n      :async:\n\n      :param str sql: SQL query to execute.\n      :param tuple params: Optional query parameters.\n      :returns: A :class:`CursorAdapter` instance.\n\n      Execute SQL asynchronously. Returns a cursor-like object whose rows are\n      already fetched (call ``.fetchall()`` synchronously). For result\n      streaming, see :meth:`~AsyncDatabaseMixin.iterate`.\n\n\n.. class:: AsyncSqliteDatabase(database, **kwargs)\n\n   Async SQLite database implementation.\n\n   Uses ``aiosqlite`` and maintains a single shared connection. Pool-related\n   configuration options are ignored.\n\n   Inherits from :class:`AsyncDatabaseMixin` and :class:`SqliteDatabase`.\n\n.. class:: AsyncPostgresqlDatabase(database, **kwargs)\n\n   Async Postgresql database implementation.\n\n   Uses ``asyncpg`` and the driver's native connection pool.\n\n   Inherits from :class:`AsyncDatabaseMixin` and :class:`PostgresqlDatabase`.\n\n.. class:: AsyncMySQLDatabase(database, **kwargs)\n\n   Async MySQL / MariaDB database implementation.\n\n   Uses ``aiomysql`` and the driver's native connection pool.\n\n   Inherits from :class:`AsyncDatabaseMixin` and :class:`MySQLDatabase`.\n\n.. class:: MissingGreenletBridge(RuntimeError)\n\n   Raised when Peewee attempts to execute a query outside a greenlet context.\n   This indicates that a query was triggered outside of ``db.run()`` or an\n   async helper call.\n"
  },
  {
    "path": "docs/peewee/contributing.rst",
    "content": ".. _contributing:\n\nContributing\n============\n\nIn order to continually improve, Peewee needs the help of developers like you.\nWhether it's contributing patches, submitting bug reports, or just asking and\nanswering questions, you are helping to make Peewee a better library.\n\nIn this document I'll describe some of the ways you can help.\n\nPatches\n-------\n\nDo you have an idea for a new feature, or is there a clunky API you'd like to\nimprove? Before coding it up and submitting a pull-request, `open a new issue\n<https://github.com/coleifer/peewee/issues/new>`_ on GitHub describing your\nproposed changes. This doesn't have to be anything formal, just a description\nof what you'd like to do and why.\n\nWhen you're ready, you can submit a pull-request with your changes. Successful\npatches will have the following:\n\n* Unit tests.\n* Documentation, both prose form and general :ref:`API documentation <api>`.\n* Code that conforms stylistically with the rest of the Peewee codebase.\n\nBugs\n----\n\nIf you've found a bug, please check to see if it has `already been reported <https://github.com/coleifer/peewee/issues/>`_,\nand if not `create an issue on GitHub <https://github.com/coleifer/peewee/issues/new>`_.\nThe more information you include, the more quickly the bug will get fixed, so\nplease try to include the following:\n\n* Traceback and the error message (please format your code).\n* Relevant portions of your code or code to reproduce the error\n* Peewee version: ``python -c \"from peewee import __version__; print(__version__)\"``\n* Which database you're using\n\nIf you have found a bug in the code and submit a failing test-case, then hats-off to you, you are a hero!\n\nQuestions\n---------\n\nIf you have questions about how to do something with peewee, then I recommend\neither:\n\n* Ask on StackOverflow. I check SO just about every day for new peewee\n  questions and try to answer them. This has the benefit also of preserving the\n  question and answer for other people to find.\n* Ask on the mailing list, https://groups.google.com/group/peewee-orm\n"
  },
  {
    "path": "docs/peewee/database.rst",
    "content": ".. _database:\n\nDatabase\n========\n\nThe Peewee :class:`Database` object represents a connection to a database.\nThe :class:`Database` class is instantiated with all the information needed\nto connect to a database.\n\nDatabase responsibilities:\n\n* :ref:`connection-lifecycle`\n* :ref:`executing-sql`\n* :ref:`Manage database schema <schema>`\n* :ref:`Manage transactions <transactions>`\n\nPeewee supports:\n\n* SQLite - :class:`SqliteDatabase` using the standard library ``sqlite3``.\n\n  .. code-block:: python\n\n     # SQLite database (use WAL journal mode and 64MB cache).\n     db = SqliteDatabase('/path/to/app.db', pragmas={\n         'journal_mode': 'wal',\n         'cache_size': -64000})\n\n* Postgresql - :class:`PostgresqlDatabase` using ``psycopg2`` or ``psycopg3``.\n\n  .. code-block:: python\n\n     db = PostgresqlDatabase(\n         'my_app',\n         user='postgres',\n         password='secret',\n         host='10.8.0.9',\n         port=5432)\n\n* MySQL and MariaDB - :class:`MySQLDatabase` using ``pymysql``.\n\n  .. code-block:: python\n\n     db = MySQLDatabase(\n         'my_app',\n         user='app',\n         password='db_password',\n         host='10.8.0.8',\n         port=3306)\n\n\nUsing SQLite\n------------\n\nTo connect to a SQLite database, use :class:`SqliteDatabase`. The first\nparameter is the filename containing the database, or the string ``':memory:'``\nto create an in-memory database.\n\nAfter the database filename specify pragmas or other `sqlite3 parameters <https://docs.python.org/3/library/sqlite3.html#sqlite3.connect>`__.\n\n.. code-block:: python\n\n   db = SqliteDatabase('my_app.db', pragmas={'journal_mode': 'wal'})\n\n   class BaseModel(Model):\n       \"\"\"Base model that will use our Sqlite database.\"\"\"\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = TextField()\n       ...\n\nSQLite-specific options are set via `pragmas <https://www.sqlite.org/pragma.html>`__.\nThe following settings are recommended for most applications:\n\n.. code-block:: python\n\n   db = SqliteDatabase('my_app.db', pragmas={\n       'journal_mode': 'wal',  # Allow readers while writer active.\n       'cache_size': -64000,  # 64 MB page cache.\n       'foreign_keys': 1,  # Enforce FK constraints.\n   })\n\n======================= =================== ================================================\nPragma                  Recommended value   Effect\n======================= =================== ================================================\n``journal_mode``        ``wal``             Allow concurrent readers and one writer.\n``cache_size``          Negative KiB value  E.g. ``-64000`` = 64 MB.\n``foreign_keys``        ``1``               Enforce ``FOREIGN KEY`` constraints.\n======================= =================== ================================================\n\n.. seealso::\n   For SQLite-specific features and extensions (JSON, full-text search), see\n   :ref:`sqlite`.\n\nUsing Postgresql\n----------------\n\nTo use Peewee with Postgresql install ``psycopg2`` or ``psycopg3``:\n\n.. code-block:: shell\n\n   pip install \"psycopg2-binary\"  # Psycopg2.\n\n   pip install \"psycopg[binary]\"  # Psycopg3.\n\nTo connect to a Postgresql database, use :class:`PostgresqlDatabase`.\nThe first parameter is always the name of the database.\n\nAfter the database name specify additional `psycopg2 <https://www.psycopg.org/docs/module.html#psycopg2.connect>`__\nor `psycopg3 <https://www.psycopg.org/psycopg3/docs/api/module.html#psycopg.connect>`__\nconnection parameters:\n\n.. code-block:: python\n\n   db = PostgresqlDatabase(\n       'my_database',\n       user='postgres',\n       password='secret',\n       host='10.8.0.1',\n       port=5432)\n\n   class BaseModel(Model):\n       \"\"\"A base model that will use our Postgresql database\"\"\"\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = CharField()\n       ...\n\nThe isolation level can be set at initialization time:\n\n.. code-block:: python\n\n   # psycopg2 or psycopg3\n   db = PostgresqlDatabase('app', isolation_level='SERIALIZABLE')\n\n   # psycopg2\n   from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE\n   db = PostgresqlDatabase('app', isolation_level=ISOLATION_LEVEL_SERIALIZABLE)\n\n   # psycopg3\n   from psycopg import IsolationLevel\n   db = PostgresqlDatabase('app', isolation_level=IsolationLevel.SERIALIZABLE)\n\n.. seealso::\n   For Postgresql-specific functionality and extensions (arrays, JSONB,\n   full-text search), see :ref:`postgresql`.\n\nUsing MySQL / MariaDB\n---------------------\n\nTo use Peewee with MySQL or MariaDB install ``pymysql``:\n\n.. code-block:: shell\n\n   pip install pymysql\n\nTo connect to a MySQL or MariaDB database, use :class:`MySQLDatabase`.\nThe first parameter is always the name of the database.\n\nAfter the database name specify additional `pymysql Connection parameters\n<https://pymysql.readthedocs.io/en/latest/modules/connections.html>`__:\n\n.. code-block:: python\n\n   db = MySQLDatabase(\n       'my_database',\n       host='10.8.0.1',\n       port=3306,\n       connection_timeout=5)\n\n   class BaseModel(Model):\n       \"\"\"A base model that will use our MySQL database\"\"\"\n       class Meta:\n           database = mysql_db\n\n   class User(BaseModel):\n       username = CharField()\n       # ...\n\nIf MySQL drops idle connections (``Error 2006: MySQL server has gone away``),\nthe solution is explicit connection management: open a connection at the start\nof each unit of work and close it when finished. See :ref:`connection-lifecycle`\nand :ref:`framework-integration`.\n\nAlternate drivers are available for both databases:\n\n* :class:`.MySQLConnectorDatabase` - uses ``mysql-connector-python``.\n* :class:`.MariaDBConnectorDatabase` - uses ``mariadb-connector-python``.\n\n.. seealso::\n   For MySQL-specific functionality and extensions, see :ref:`mysql`.\n\nConnection Parameters\n---------------------\n\n:class:`Database` initialization methods expect the name of the database as the\nfirst parameter. Subsequent keyword arguments are passed to the underlying\ndatabase driver when establishing the connection.\n\nWith Postgresql it is common to need to specify the ``host``, ``user`` and\n``password`` when creating a connection. These should be specified when\ninitializing the database, and they will be passed directly back to\n``psycopg`` when creating connections:\n\n.. code-block:: python\n\n    db = PostgresqlDatabase(\n        'database_name',  # Required by Peewee.\n        user='postgres',  # Will be passed directly to psycopg.\n        password='secret',  # Ditto.\n        host='db.mysite.com')  # Ditto.\n\nAs another example, the ``pymysql`` driver accepts a ``charset`` parameter\nwhich is not a standard Peewee :class:`Database` parameter. To set this\nvalue, pass in ``charset`` alongside your other settings:\n\n.. code-block:: python\n\n   db = MySQLDatabase('database_name', user='www-data', charset='utf8mb4')\n\nConsult your database driver's documentation for the available parameters:\n\n* Postgresql: `psycopg2 <https://www.psycopg.org/docs/module.html#psycopg2.connect>`__\n  or `psycopg3 <https://www.psycopg.org/psycopg3/docs/api/module.html#psycopg.connect>`__\n* MySQL: `pymysql <https://github.com/PyMySQL/PyMySQL/blob/f08f01fe8a59e8acfb5f5add4a8fe874bec2a196/pymysql/connections.py#L494-L513>`__\n* SQLite: `sqlite3 <https://docs.python.org/3/library/sqlite3.html#sqlite3.connect>`__\n\n.. _initializing-database:\n\nInitializing the Database\n-------------------------\n\nThere are three ways to initialize a database:\n\n1. **Initialize database directly**. Use when connection settings are available at\n   the time the database is declared:\n\n   .. code-block:: python\n\n      db = SqliteDatabase('/path/to/app.db')\n\n   Environment variables, config settings, etc. typically fall into this\n   category as well:\n\n   .. code-block:: python\n\n      import os\n\n      db = PostgresqlDatabase(\n          database=app.config['APP_NAME'],\n          user=os.environ.get('PGUSER') or 'postgres',\n          host=os.environ.get('PGHOST') or '127.0.0.1')\n\n2. **Defer initialization**. This method is needed when a connection setting is not\n   available until run-time **or** it is inconvenient to import connection settings\n   where the database is declared:\n\n   .. code-block:: python\n\n      db = PostgresqlDatabase(None)\n\n      # ... some time later ...\n      db_name = input('Enter database name: ')\n\n      # Initialize the database now.\n      db.init(db_name, user='postgres', host='10.8.0.1')\n\n   Attempting to use an uninitialized database will raise an :class:`InterfaceError`.\n\n3. **Proxy**. Use a :class:`DatabaseProxy` and set the database at run-time.\n   This method is needed when the database implementation may change at\n   run-time. For example it may be either Sqlite or Postgresql depending on a\n   command-line option:\n\n   .. code-block:: python\n\n      db = DatabaseProxy()\n\n      # ... some time later ...\n      if app.config['DEBUG']:\n          database = SqliteDatabase('local.db')\n      elif app.config['TESTING']:\n          database = SqliteDatabase(':memory:')\n      else:\n          database = PostgresqlDatabase('production')\n\n      db.initialize(database)\n\n   Attempting to use an uninitialized database proxy will raise an ``AttributeError``.\n\n.. _binding_database:\n\nChanging the database at run-time\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nPeewee can also set or change the database at run-time in a different way. This\ntechnique is used by the Peewee test suite to **bind** test model classes to\nvarious database instances when running tests.\n\nThere are two sets of complementary methods:\n\n* :meth:`Database.bind` and :meth:`Model.bind` - bind one or more models\n  to a database.\n* :meth:`Database.bind_ctx` and :meth:`Model.bind_ctx` - which are the\n  same as their ``bind()`` counterparts, but return a context-manager and are\n  useful when the database should only be changed temporarily.\n\nAs an example, we'll declare two models **without** specifying any database:\n\n.. code-block:: python\n\n   class User(Model):\n       username = TextField()\n\n   class Tweet(Model):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = TimestampField()\n\nBind the models to a database at run-time:\n\n.. code-block:: python\n   :emphasize-lines: 7, 10\n\n   postgres_db = PostgresqlDatabase('my_app', user='postgres')\n   sqlite_db = SqliteDatabase('my_app.db')\n\n   # At this point, the User and Tweet models are NOT bound to any database.\n\n   # Bind them to the Postgres database:\n   postgres_db.bind([User, Tweet])\n\n   # Temporarily bind them to the sqlite database:\n   with sqlite_db.bind_ctx([User, Tweet]):\n       # User and Tweet are now bound to the sqlite database.\n       assert User._meta.database is sqlite_db\n\n   # User and Tweet are once again bound to the Postgres database.\n   assert User._meta.database is postgres_db\n\nThe :meth:`Model.bind` and :meth:`Model.bind_ctx` methods work the same\nfor binding a given model class:\n\n.. code-block:: python\n   :emphasize-lines: 3, 9\n\n   # Bind the user model to the sqlite db. By default, Peewee will also\n   # bind any models that are related to User via foreign-key as well.\n   User.bind(sqlite_db)\n\n   assert User._meta.database is sqlite_db\n   assert Tweet._meta.database is sqlite_db  # Related models bound too.\n\n   # Temporarily bind *just* the User model to the postgres db.\n   with User.bind_ctx(postgres_db, bind_backrefs=False):\n       assert User._meta.database is postgres_db\n       assert Tweet._meta.database is sqlite_db  # Has not changed.\n\n   # User is back to being bound to the sqlite_db.\n   assert User._meta.database is sqlite_db\n\nPeewee database connections are thread-safe. However, if you plan to **bind**\nthe database at run-time in a multi-threaded application, storing the model's\ndatabase in a thread-local is necessary. This can be accomplished with\nthe :class:`~playhouse.shortcuts.ThreadSafeDatabaseMetadata`.\n\n.. code-block:: python\n\n   from peewee import *\n   from playhouse.shortcuts import ThreadSafeDatabaseMetadata\n\n   class BaseModel(Model):\n       class Meta:\n           model_metadata_class = ThreadSafeDatabaseMetadata\n\nThe database can now be swapped safely while running in a multi-threaded\nenvironment using the :meth:`Database.bind` or :meth:`Database.bind_ctx`.\n\nConnecting via URL\n------------------\n\nThe :ref:`db-url` playhouse module provides a :func:`~playhouse.db_url.connect`\nhelper that accepts a database URL and returns the appropriate database\ninstance:\n\n.. code-block:: python\n\n   from playhouse.db_url import connect\n\n   db = connect(os.environ.get('DATABASE_URL', 'sqlite:///local.db'))\n\nExample URLs:\n\n* ``sqlite:///my_app.db`` - SQLite file in the current directory.\n* ``sqlite:///:memory:`` - in-memory SQLite.\n* ``sqlite:////absolute/path/to/app.db`` - absolute path SQLite.\n* ``postgresql://user:password@host:5432/dbname``\n* ``mysql://user:password@host:3306/dbname``\n* :ref:`More examples in the db_url documentation <db-url>`.\n\n.. _connection-lifecycle:\n\nConnection Lifecycle\n--------------------\n\nApplications will generally fall into two categories:\n\n* Single-user applications which open a connection at startup and close at\n  exit.\n* Multi-user or web appliactions, which open a connection per request and close\n  it at the end of the request.\n\nTo open a connection to a database, use the :meth:`Database.connect` method:\n\n.. code-block:: pycon\n\n   >>> db = SqliteDatabase(':memory:')  # In-memory SQLite database.\n   >>> db.connect()\n   True\n\n   >>> pass  # ... do work ...\n\n   >>> db.close()\n   True\n\nIf you call ``connect()`` on an already-open database, an :exc:`OperationalError`\nis raised. Pass ``reuse_if_open=True`` to suppress it:\n\n.. code-block:: pycon\n\n   >>> db.connect(reuse_if_open=True)\n\nTo close a connection, use the :meth:`Database.close` method:\n\n.. code-block:: pycon\n\n   >>> db.close()\n   True\n\nCalling ``close()`` on an already-closed connection will not result in an\nexception, but will return ``False``:\n\n.. code-block:: pycon\n\n    >>> db.connect()  # Open connection.\n    True\n    >>> db.close()  # Close connection.\n    True\n    >>> db.close()  # Connection already closed, returns False.\n    False\n\nDetermine whether the database is closed using the :meth:`Database.is_closed`\nmethod:\n\n.. code-block:: pycon\n\n   >>> db.is_closed()\n   True\n\nWeb applications will typically use framework-provided hooks to manage\nconnection lifecycles.\n\n.. code-block:: python\n\n   @app.before_request\n   def _db_connect():\n       db.connect()\n\n   @app.teardown_request\n   def _db_close(exc):\n       if not db.is_closed():\n           db.close()\n\nSee :ref:`framework-integration` for framework-specific examples.\n\n.. tip::\n   Peewee uses thread local storage to manage connection state, so this\n   pattern can be used with multi-threaded or gevent applications.\n\n   Peewee's :ref:`asyncio integration <pwasyncio>` stores connection state in\n   task-local storage, so the same pattern applies.\n\nContext managers\n^^^^^^^^^^^^^^^^\n\nThe database object can be used as a context manager or decorator.\n\n1. Connection opens when context manager is entered.\n2. Peewee begins a transaction.\n3. Control is passed to user for duration of block.\n4. Peewee commits transaction if block exits cleanly, otherwise issues a\n   rollback.\n5. Peewee closes the connection.\n6. Any unhandled exception is raised.\n\n.. code-block:: python\n\n   with db:\n       User.create(username='charlie')\n       # Transaction is committed when the block exits normally,\n       # rolled back if an exception is raised.\n\nDecorator:\n\n.. code-block:: python\n\n   @db\n   def demo():\n       print('closed?', db.is_closed())\n\n   demo()  # \"closed? False\"\n   db.is_closed()  # True\n\nTo manage the connection lifetime without an implicit transaction, use\n:meth:`~Database.connection_context`:\n\n.. code-block:: python\n\n   with db.connection_context():\n       # Connection is open; no implicit transaction.\n       results = User.select()\n\n``connection_context()`` can also decorate a function:\n\n.. code-block:: python\n\n   @db.connection_context()\n   def load_fixtures():\n       db.create_tables([User, Tweet])\n       import_data()\n\nUsing autoconnect\n^^^^^^^^^^^^^^^^^\n\nBy default Peewee will automatically open a connection if one is not available.\nThis behavior is controlled by the ``autoconnect`` Database parameter. Managing\nconnections explicitly is considered a **best practice**, therefore\nconsider disabling the ``autoconnect`` behavior:\n\n.. code-block:: python\n\n   db = PostgresqlDatabase('app', autoconnect=False)\n\nIt is helpful to be explicit about connection lifetimes. If a connection cannot\nbe opened, the exception will be caught when the connection is being opened,\nrather than at query time.\n\nThread safety\n^^^^^^^^^^^^^\n\nDatabase connections and associated transactions are thread-safe.\n\nPeewee keeps track of the connection state using thread-local storage, making\nthe Peewee :class:`Database` object safe to use with multiple threads. Each\nthread will have it's own connection, and as a result any given thread will\nonly have a single connection open at a given time.\n\nPeewee's :ref:`asyncio integration <pwasyncio>` stores connection state in\ntask-local storage, so the same applies to async applications.\n\nDB-API Connection object\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`Database.connection` returns a reference to the underlying DB-API driver\nconnection. This method will return the currently-open connection object, if\none exists, otherwise it will open a new connection.\n\n.. code-block:: pycon\n\n   >>> db.connection()\n   <sqlite3.Connection object at 0x7f94e9362f10>\n\n.. _connection-pooling:\n\nConnection Pooling\n------------------\n\nFor web applications that handle many requests, opening and closing a database\nconnection on every request adds latency. A connection pool keeps a set of\nconnections open and lends them out as needed.\n\nPooled database classes are available in :ref:`playhouse.pool <pool>`:\n\n.. code-block:: python\n\n   from playhouse.pool import PooledPostgresqlDatabase\n\n   db = PooledPostgresqlDatabase(\n       'my_app',\n       user='postgres',\n       max_connections=20,\n       stale_timeout=300,   # Recycle connections idle for 5 minutes.\n   )\n\n.. include:: pool-snippet.rst\n\nWhen using a connection pool, :meth:`~Database.connect` and :meth:`~Database.close`\ndo not open and close real connections - they acquire and release connections\nfrom the pool. It is therefore essential to call both explicitly (or use a\ncontext manager) so connections are returned to the pool for re-use.\n\n.. seealso:: :ref:`pool`\n\n.. _executing-sql:\n\nExecuting SQL\n-------------\n\nSQL queries will typically be executed by calling ``execute()`` on a query\nconstructed using the query-builder APIs (or by simply iterating over a query\nobject in the case of a :class:`Select` query). For cases where you wish to\nexecute SQL directly, use the :meth:`Database.execute_sql`:\n\n.. code-block:: python\n\n   db = SqliteDatabase('my_app.db')\n   db.connect()\n\n   # Example of executing a simple query and ignoring the results.\n   db.execute_sql(\"ATTACH DATABASE ':memory:' AS cache;\")\n\n   # Example of iterating over the results of a query using the cursor.\n   cursor = db.execute_sql('SELECT * FROM users WHERE status = ?', (ACTIVE,))\n   for row in cursor.fetchall():\n       # Do something with row, which is a tuple containing column data.\n       pass\n\n.. _database-errors:\n\nDatabase Errors\n---------------\n\nThe Python DB-API 2.0 spec describes `several types of exceptions <https://www.python.org/dev/peps/pep-0249/#exceptions>`_.\nBecause most database drivers have their own implementations of these\nexceptions, Peewee simplifies things by providing its own wrappers around any\nimplementation-specific exception classes. That way, you don't need to worry\nabout dealing with driver-specific exception classes, you can just use the ones\nfrom peewee:\n\n* :class:`DatabaseError`\n* :class:`DataError`\n* :class:`IntegrityError`\n* :class:`InterfaceError`\n* :class:`InternalError`\n* :class:`NotSupportedError`\n* :class:`OperationalError`\n* :class:`ProgrammingError`\n\nAll of these error classes extend :class:`PeeweeException`.\n\nLogging Queries\n---------------\n\nPeewee logs every query to the ``peewee`` namespace at ``DEBUG`` level using\nthe standard library ``logging`` module:\n\n.. code-block:: python\n\n   import logging\n   logging.getLogger('peewee').addHandler(logging.StreamHandler())\n   logging.getLogger('peewee').setLevel(logging.DEBUG)\n\nThis is the simplest way to verify what queries are being issued during\ndevelopment.\n\n.. _testing:\n\nTesting Peewee Applications\n---------------------------\n\nWhen writing tests for an application that uses Peewee, it may be desirable to\nuse a special database for tests. Another common practice is to run tests\nagainst a clean database, which means ensuring tables are empty at the start of\neach test.\n\nBinding models to a database at run-time is described here: :ref:`binding_database`.\n\nExample test-case setup:\n\n.. code-block:: python\n\n   # tests.py\n   import unittest\n   from my_app.models import EventLog, Relationship, Tweet, User\n\n   MODELS = [User, Tweet, EventLog, Relationship]\n\n   # use an in-memory SQLite for tests.\n   test_db = SqliteDatabase(':memory:')\n\n   class BaseTestCase(unittest.TestCase):\n       def setUp(self):\n           # Bind model classes to test db. Since we have a complete list of\n           # all models, we do not need to recursively bind dependencies.\n           test_db.bind(MODELS, bind_refs=False, bind_backrefs=False)\n\n           test_db.connect()\n           test_db.create_tables(MODELS)\n\n       def tearDown(self):\n           # Not strictly necessary since SQLite in-memory databases only live\n           # for the duration of the connection, and in the next step we close\n           # the connection...but a good practice all the same.\n           test_db.drop_tables(MODELS)\n\n           # Close connection to db.\n           test_db.close()\n\n           # If we wanted, we could re-bind the models to their original\n           # database here. But for tests this is probably not necessary.\n\nIt is recommended to test using the same database backend used in production,\nso as to avoid any potential compatibility issues.\n\n.. seealso::\n   * :ref:`test-utils`\n   * Peewee's `test-suite <https://github.com/coleifer/peewee/tree/master/tests>`__\n\nAdding a Custom Database Driver\n---------------------------------\n\nIf your database driver conforms to DB-API 2.0, adding Peewee support requires\nsubclassing :class:`Database` and overriding ``_connect``, which must return\na connection in autocommit mode:\n\n.. code-block:: python\n\n   from peewee import Database\n   import foodb\n\n   class FooDatabase(Database):\n       def _connect(self):\n           return foodb.connect(self.database, autocommit=True,\n                                **self.connect_params)\n\n       def get_tables(self):\n           res = self.execute_sql('SHOW TABLES;')\n           return [r[0] for r in res.fetchall()]\n\nThe minimum Peewee relies on from the driver is: ``Connection.commit``,\n``Connection.rollback``, ``Connection.execute``, ``Cursor.description``, and\n``Cursor.fetchone``. Everything else can be incrementally added.\n\nOther integration points on :class:`Database`:\n\n* ``param`` / ``quote`` - parameter placeholder and quoting characters.\n* ``field_types`` - mapping from Peewee type labels to vendor column types.\n* ``operations`` - mapping from operations such as ``ILIKE`` to vendor SQL.\n\nRefer to the :class:`Database` API reference or the `Peewee source\n<https://github.com/coleifer/peewee/blob/master/peewee.py>`_ for details.\n"
  },
  {
    "path": "docs/peewee/db_tools.rst",
    "content": ".. _db-tools:\n\nDatabase Tooling\n================\n\nThis section covers the playhouse modules for managing connections, database\nURLs, schema migrations, introspection, code generation, and testing.\n\n.. contents:: On this page\n   :local:\n   :depth: 1\n\n\n.. _db-url:\n\nDatabase URLs\n-------------\n\n.. module:: playhouse.db_url\n\nThe ``playhouse.db_url`` module lets you configure Peewee from a connection\nstring, which is common in twelve-factor applications where database\ncredentials live in environment variables.\n\n.. code-block:: python\n\n   import os\n   from playhouse.db_url import connect\n\n   db = connect(os.environ.get('DATABASE_URL', 'sqlite:////default.db'))\n\nPass additional keyword arguments in the query string:\n\n.. code-block:: python\n\n   db = connect('postgres://user:pass@host/db?max_connections=20')\n\nURL format: ``scheme://user:password@host:port/dbname?option=value``\n\nCommon schemes:\n\n+------------------------+------------------------------------------+\n| Scheme                 | Database class                           |\n+========================+==========================================+\n| ``sqlite:///path``     | :class:`SqliteDatabase`                  |\n+------------------------+------------------------------------------+\n| ``postgres://``        | :class:`PostgresqlDatabase`              |\n+------------------------+------------------------------------------+\n| ``postgresext://``     | :class:`.PostgresqlExtDatabase`          |\n+------------------------+------------------------------------------+\n| ``mysql://``           | :class:`MySQLDatabase`                   |\n+------------------------+------------------------------------------+\n\nConnection pool implementations:\n\n+-----------------------------+------------------------------------------+\n| Scheme                      | Database class                           |\n+=============================+==========================================+\n| ``sqlite+pool:///path``     | :class:`.PooledSqliteDatabase`           |\n+-----------------------------+------------------------------------------+\n| ``postgres+pool://``        | :class:`.PooledPostgresqlDatabase`       |\n+-----------------------------+------------------------------------------+\n| ``postgresext+pool://``     | :class:`.PooledPostgresqlExtDatabase`    |\n+-----------------------------+------------------------------------------+\n| ``mysql+pool://``           | :class:`.PooledMySQLDatabase`            |\n+-----------------------------+------------------------------------------+\n\nAlternate drivers:\n\n+------------------------------+------------------------------------------+\n| Scheme                       | Database class                           |\n+==============================+==========================================+\n| ``psycopg3://``              | :class:`.Psycopg3Database`               |\n+------------------------------+------------------------------------------+\n| ``psycopg3+pool://``         | :class:`.PooledPsycopg3Database`         |\n+------------------------------+------------------------------------------+\n| ``cockroachdb://``           | :class:`.CockroachDatabase`              |\n+------------------------------+------------------------------------------+\n| ``cockroachdb+pool://``      | :class:`.PooledCockroachDatabase`        |\n+------------------------------+------------------------------------------+\n| ``cysqlite://``              | :class:`.CySqliteDatabase`               |\n+------------------------------+------------------------------------------+\n| ``cysqlite+pool://``         | :class:`.PooledCySqliteDatabase`         |\n+------------------------------+------------------------------------------+\n| ``apsw://``                  | :class:`.APSWDatabase`                   |\n+------------------------------+------------------------------------------+\n| ``mariadbconnector://``      | :class:`.MariaDBConnectorDatabase`       |\n+------------------------------+------------------------------------------+\n| ``mariadbconnector+pool://`` | :class:`.PooledMariaDBConnectorDatabase` |\n+------------------------------+------------------------------------------+\n| ``mysqlconnector://``        | :class:`.MySQLConnectorDatabase`         |\n+------------------------------+------------------------------------------+\n| ``mysqlconnector+pool://``   | :class:`.PooledMySQLConnectorDatabase`   |\n+------------------------------+------------------------------------------+\n\n\n.. function:: connect(url, unquote_password=False, unquote_user=False, **connect_params)\n\n   :param url: the URL for the database, see examples.\n   :param bool unquote_password: unquote special characters in the password.\n   :param bool unquote_user: unquote special characters in the user.\n   :param connect_params: additional parameters to pass to the Database.\n\n   Parse ``url`` and return an appropriate :class:`Database` instance.\n\n   Examples:\n\n   * ``sqlite:///my_app.db`` - SQLite file in the current directory.\n   * ``sqlite:///:memory:`` - in-memory SQLite.\n   * ``sqlite:////absolute/path/to/app.db`` - absolute path SQLite.\n   * ``postgresql://user:password@host:5432/dbname``\n   * ``mysql://user:password@host:3306/dbname``\n\n.. function:: parse(url, unquote_password=False, unquote_user=False)\n\n   :param url: the URL for the database, see :func:`connect` above for examples.\n   :param bool unquote_password: unquote special characters in the password.\n   :param bool unquote_user: unquote special characters in the user.\n\n   Parse a URL and return a dictionary with ``database``, ``host``,\n   ``port``, ``user``, and ``password`` keys plus any extra connect\n   parameters from the query string.\n\n   Useful if you need to construct a database class manually:\n\n   .. code-block:: python\n\n       params = parse('postgres://user:pass@host:5432/mydb')\n       db = MyCustomDatabase(**params)\n\n.. function:: register_database(db_class, *names)\n\n   :param db_class: A subclass of :class:`Database`.\n   :param names: A list of names to use as the scheme in the URL.\n\n   Register a custom database class under one or more URL scheme names so\n   that :func:`connect` can instantiate it:\n\n   .. code-block:: python\n\n       register_database(FirebirdDatabase, 'firebird')\n       db = connect('firebird://my-firebird-db')\n\n\n.. _pool:\n\nConnection Pooling\n------------------\n\n.. module:: playhouse.pool\n\nThe ``playhouse.pool`` module contains a number of :class:`Database` classes\nthat provide connection pooling for Postgresql, MySQL and SQLite databases. The\npool works by overriding the methods on the :class:`Database` class that open\nand close connections to the backend.\n\nIn multi-threaded applications, each thread gets its own connection; the\npool maintains up to ``max_connections`` open connections at any time.\nIn single-threaded applications, a single connection is recycled.\n\nThe application only needs to ensure that connections are *closed* when work\nis done - typically at the end of an HTTP request. Closing a pooled connection\nreturns it to the pool rather than actually disconnecting.\n\n.. code-block:: python\n\n   from playhouse.pool import PooledPostgresqlDatabase\n\n   db = PooledPostgresqlDatabase(\n       'my_app',\n       user='postgres',\n       max_connections=32,\n       stale_timeout=300)\n\n.. tip::\n   Pooled database implementations may be safely used as drop-in replacements\n   for their non-pooled counterparts.\n\n.. include:: pool-snippet.rst\n\n.. note::\n   Applications using Peewee's :ref:`asyncio integration <pwasyncio>` do not need to\n   use a special pooled database - the Async databases use a connection pool by\n   default.\n\n.. class:: PooledDatabase(database, max_connections=20, stale_timeout=None, timeout=None, **kwargs)\n\n   Mixin class mixed into the specific backend subclasses above.\n\n   :param str database: The name of the database or database file.\n   :param int max_connections: Maximum number of concurrent connections.\n       Pass ``None`` for no limit.\n   :param int stale_timeout: Seconds after which an idle connection is\n       considered stale and will be discarded next time it would be reused.\n   :param int timeout: Seconds to block when all connections are in use.\n       ``0`` blocks indefinitely; ``None`` (default) raises immediately.\n\n   .. note::\n      Connections will not be closed exactly when they exceed their\n      ``stale_timeout``. Instead, stale connections are only closed when a new\n      connection is requested.\n\n   .. note::\n       If the pool is exhausted and no ``timeout`` is configured, a\n       ``ValueError`` is raised.\n\n   .. method:: manual_close()\n\n      Close the current connection permanently without returning it to the\n      pool. Use this when a connection has entered a bad state.\n\n   .. method:: close_idle()\n\n      Close all pooled connections that are not currently in use.\n\n   .. method:: close_stale(age=600)\n\n      :param int age: Age at which a connection should be considered stale.\n      :returns: Number of connections closed.\n\n      Close in-use connections that have exceeded ``age`` seconds.\n      Use with caution.\n\n   .. method:: close_all()\n\n      Close all connections including those currently in use.\n      Use with caution.\n\n.. class:: PooledSqliteDatabase(database, max_connections=20, stale_timeout=None, timeout=None, **kwargs)\n\n   Pool implementation for SQLite databases. Extends :class:`SqliteDatabase`.\n\n.. class:: PooledPostgresqlDatabase(database, max_connections=20, stale_timeout=None, timeout=None, **kwargs)\n\n   Pool implementation for Postgresql databases. Extends :class:`PostgresqlDatabase`.\n\n.. class:: PooledMySQLDatabase(database, max_connections=20, stale_timeout=None, timeout=None, **kwargs)\n\n   Pool implementation for MySQL / MariaDB databases. Extends :class:`MySQLDatabase`.\n\n\n.. _migrate:\n\nSchema Migrations\n-----------------\n\n.. module:: playhouse.migrate\n\nThe ``playhouse.migrate`` module provides a lightweight API for making\nincremental schema changes to an existing database without writing raw SQL.\n\nThe peewee migration philosophy is that tools relying on database\nintrospection, versioning, and auto-detection are often fragile, brittle and\nunnecessarily complex. Migrations can be written as simple python scripts and\nexecuted from the command-line. Since the migrations only depend on your\napplication's :class:`Database` object, migration scripts to not introduce new\ndependencies.\n\nSupported operations:\n\n- Add, rename, or drop columns.\n- Make columns nullable or not nullable.\n- Change a column's type.\n- Rename a table.\n- Add or drop indexes and constraints.\n- Add or drop column default values.\n\n.. seealso:: :ref:`schema`\n\n.. code-block:: python\n\n   from playhouse.migrate import SchemaMigrator, migrate\n\n   migrator = SchemaMigrator.from_database(db)\n\n   with db.atomic():\n       migrate(\n           migrator.add_column('tweet', 'is_published', BooleanField(default=True)),\n           migrator.add_column('user', 'email', CharField(null=True)),\n           migrator.drop_column('user', 'old_bio'),\n       )\n\n.. tip::\n   Wrap migrations in ``db.atomic()`` to ensure changes are not partially\n   applied.\n\nOperations\n^^^^^^^^^^\n\n**Add columns:**\n\n.. code-block:: python\n\n   # Non-null fields must supply a default value.\n   migrate(\n       migrator.add_column('comment', 'pub_date', DateTimeField(null=True)),\n       migrator.add_column('comment', 'body', TextField(default='')),\n   )\n\n**Add a foreign key** (the column name must include the ``_id`` suffix that\nPeewee appends by default):\n\n.. code-block:: python\n\n   user_fk = ForeignKeyField(User, field=User.id, null=True)\n   migrate(\n       migrator.add_column('tweet', 'user_id', user_fk),\n   )\n\n**Rename a column:**\n\n.. code-block:: python\n\n   migrate(\n       migrator.rename_column('story', 'pub_date', 'publish_date'),\n       migrator.rename_column('story', 'mod_date', 'modified_date'),\n   )\n\n**Drop a column:**\n\n.. code-block:: python\n\n   migrate(migrator.drop_column('story', 'old_field'))\n\n**Nullable / not nullable:**\n\n.. code-block:: python\n\n   migrate(\n       migrator.drop_not_null('story', 'pub_date'),  # Allow NULLs.\n       migrator.add_not_null('story', 'modified_date'),  # Disallow NULLs.\n   )\n\n**Change type:**\n\n.. code-block:: python\n\n   # Change a VARCHAR(...) to a TEXT field.\n   migrate(migrator.alter_column_type('person', 'email', TextField()))\n\n**Rename table:**\n\n.. code-block:: python\n\n   migrate(migrator.rename_table('story', 'stories'))\n\n**Add / drop indexes:**\n\n.. code-block:: python\n\n   # Specify table, column(s), and unique/non-unique.\n   migrate(\n       # Create an index on the `pub_date` column.\n       migrator.add_index('story', ('pub_date',), False),  # Normal index.\n\n       # Create a unique index on the category and title fields.\n       migrator.add_index('story', ('category_id', 'title'), True),  # Unique.\n\n       # Drop the pub-date + status index.\n       migrator.drop_index('story', 'story_pub_date_status'),\n   )\n\n**Add / drop constraints:**\n\n.. code-block:: python\n\n   from peewee import Check\n\n   # Add a CHECK() constraint to enforce the price cannot be negative.\n   migrate(migrator.add_constraint(\n       'products',\n       'price_check',\n       Check('price >= 0')))\n\n   # Remove the price check constraint.\n   migrate(migrator.drop_constraint('products', 'price_check'))\n\n   # Add a UNIQUE constraint on the first and last names.\n   migrate(migrator.add_unique('person', 'first_name', 'last_name'))\n\n**Column defaults:**\n\n.. code-block:: python\n\n   # Add a default value:\n   migrate(migrator.add_column_default('entry', 'status', 'draft'))\n\n   # Use a function (not supported in SQLite):\n   migrate(migrator.add_column_default('entry', 'created_at', fn.NOW()))\n\n   # SQLite-compatible function syntax:\n   migrate(migrator.add_column_default('entry', 'created_at', 'now()'))\n\n   # Remove a default:\n   migrate(migrator.drop_column_default('entry', 'status'))\n\n.. note::\n   Postgres users may need to set the search-path when using a non-standard\n   schema. This can be done as follows:\n\n   .. code-block:: python\n\n      migrator = PostgresqlMigrator(db)\n      migrate(\n          migrator.set_search_path('my_schema'),\n          migrator.add_column('table', 'field', TextField(default='')),\n      )\n\nMigration API\n^^^^^^^^^^^^^\n\n.. function:: migrate(*operations)\n\n   Execute one or more schema-altering operations.\n\n   Usage:\n\n   .. code-block:: python\n\n       migrate(\n           migrator.add_column('t', 'col', CharField(default='')),\n           migrator.add_index('t', ('col',), False),\n       )\n\n.. class:: SchemaMigrator(database)\n\n   :param database: a :class:`Database` instance.\n\n   The :class:`SchemaMigrator` is responsible for generating schema-altering\n   statements.\n\n   .. classmethod:: from_database(database)\n\n      :param Database database: database instance to generate migrations for.\n      :return: :class:`SchemaMigrator` instance appropriate to provided database.\n\n      Factory method that returns the appropriate :class:`SchemaMigrator`\n      subclass for the given database.\n\n   .. method:: add_column(table, column_name, field)\n\n      :param str table: Name of the table to add column to.\n      :param str column_name: Name of the new column.\n      :param Field field: A :class:`Field` instance.\n\n      Add a new column to the provided table. The ``field`` provided will be used\n      to generate the appropriate column definition.\n\n      If the field is not nullable it must specify a default value.\n\n      .. note::\n         For non-null columns, the following occurs:\n\n         1. column is added as allowing NULLs\n         2. ``UPDATE`` query is executed to populate the default value\n         3. column is changed to NOT NULL\n\n   .. method:: drop_column(table, column_name, cascade=True)\n\n      :param str table: Name of the table to drop column from.\n      :param str column_name: Name of the column to drop.\n      :param bool cascade: Whether the column should be dropped with `CASCADE`.\n\n   .. method:: rename_column(table, old_name, new_name)\n\n      :param str table: Name of the table containing column to rename.\n      :param str old_name: Current name of the column.\n      :param str new_name: New name for the column.\n\n   .. method:: add_not_null(table, column)\n\n      :param str table: Name of table containing column.\n      :param str column: Name of the column to make not nullable.\n\n   .. method:: drop_not_null(table, column)\n\n      :param str table: Name of table containing column.\n      :param str column: Name of the column to make nullable.\n\n   .. method:: add_column_default(table, column, default)\n\n      :param str table: Name of table containing column.\n      :param str column: Name of the column to add default to.\n      :param default: New default value for column. See notes below.\n\n      Peewee attempts to properly quote the default if it appears to be a\n      string literal. Otherwise the default will be treated literally.\n      Postgres and MySQL support specifying the default as a peewee\n      expression, e.g. ``fn.NOW()``, but Sqlite users will need to use\n      ``default='now()'`` instead.\n\n   .. method:: drop_column_default(table, column)\n\n      :param str table: Name of table containing column.\n      :param str column: Name of the column to remove default from.\n\n   .. method:: alter_column_type(table, column, field, cast=None)\n\n      :param str table: Name of the table.\n      :param str column_name: Name of the column to modify.\n      :param Field field: :class:`Field` instance representing new\n          data type.\n      :param cast: (postgres-only) specify a cast expression if the\n          data-types are incompatible, e.g. ``column_name::int``. Can be\n          provided as either a string or a :class:`Cast` instance.\n\n      Alter the data-type of a column. This method should be used with care,\n      as using incompatible types may not be well-supported by your database.\n\n   .. method:: rename_table(old_name, new_name)\n\n      :param str old_name: Current name of the table.\n      :param str new_name: New name for the table.\n\n   .. method:: add_index(table, columns, unique=False, using=None)\n\n      :param str table: Name of table on which to create the index.\n      :param list columns: List of columns which should be indexed.\n      :param bool unique: Whether the new index should specify a unique constraint.\n      :param str using: Index type (where supported), e.g. GiST or GIN.\n\n   .. method:: drop_index(table, index_name)\n\n      :param str table: Name of the table containing the index to be dropped.\n      :param str index_name: Name of the index to be dropped.\n\n   .. method:: add_constraint(table, name, constraint)\n\n      :param str table: Table to add constraint to.\n      :param str name: Name used to identify the constraint.\n      :param constraint: either a :func:`Check` constraint or for\n          adding an arbitrary constraint use :class:`SQL`.\n\n   .. method:: drop_constraint(table, name)\n\n      :param str table: Table to drop constraint from.\n      :param str name: Name of constraint to drop.\n\n   .. method:: add_unique(table, *column_names)\n\n      :param str table: Table to add constraint to.\n      :param str column_names: One or more columns for UNIQUE constraint.\n\n.. class:: PostgresqlMigrator(database)\n\n   .. method:: set_search_path(schema_name)\n\n      Set the Postgres search path for subsequent operations.\n\n.. class:: SqliteMigrator(database)\n\n   SQLite has limited support for ``ALTER TABLE`` queries, so the following\n   operations are currently not supported for SQLite:\n\n   * ``add_constraint``\n   * ``drop_constraint``\n   * ``add_unique``\n\n.. class:: MySQLMigrator(database)\n\n   MySQL-specific subclass.\n\n\n.. _reflection:\n\nReflection\n----------\n\n.. module:: playhouse.reflection\n\nThe ``playhouse.reflection`` module introspects an existing database and\ngenerates Peewee model classes from its schema. It is used internally by\n:ref:`pwiz` and :ref:`dataset`.\n\n.. code-block:: python\n\n   from playhouse.reflection import generate_models\n\n   db = PostgresqlDatabase('my_app')\n   models = generate_models(db)   # Returns {table_name: ModelClass}\n\n   # list(models.keys())\n   # ['account', 'customer', 'order', 'orderitem', 'product']\n\n   # Get a reference to a generated model.\n   Customer = models['customer']\n\n   # Or inject into the current namespace:\n   # globals().update(models)\n\n   # Query generated models:\n   for customer in Customer.select():\n       print(customer.name, customer.email)\n\n.. function:: generate_models(database, schema=None, **options)\n\n   :param Database database: database instance to introspect.\n   :param str schema: optional schema to introspect.\n   :param options: arbitrary options, see :meth:`Introspector.generate_models` for details.\n   :returns: a ``dict`` mapping table names to model classes.\n\n.. function:: print_model(model)\n\n   Print a human-readable summary of a model's fields and indexes to\n   stdout. Useful for interactive exploration:\n\n   .. code-block:: pycon\n\n      >>> print_model(Tweet)\n      tweet\n        id AUTO PK\n        user INT FK: User.id\n        content TEXT\n        timestamp DATETIME\n\n      index(es)\n        user_id\n        timestamp\n\n.. function:: print_table_sql(model)\n\n   Print the ``CREATE TABLE`` SQL for a model class (without indexes or\n   constraints):\n\n   .. code-block:: pycon\n\n      >>> print_table_sql(Tweet)\n      CREATE TABLE IF NOT EXISTS \"tweet\" (\n        \"id\" INTEGER NOT NULL PRIMARY KEY,\n        \"user_id\" INTEGER NOT NULL,\n        \"content\" TEXT NOT NULL,\n        \"timestamp\" DATETIME NOT NULL,\n        FOREIGN KEY (\"user_id\") REFERENCES \"user\" (\"id\")\n      )\n\n.. class:: Introspector(metadata, schema=None)\n\n   Metadata can be extracted from a database by instantiating an :class:`Introspector`.\n   Rather than instantiating this class directly, it is recommended to use the\n   factory method :meth:`~Introspector.from_database`.\n\n   .. classmethod:: from_database(database, schema=None)\n\n      :param database: a :class:`Database` instance.\n      :param str schema: an optional schema (supported by some databases).\n\n      Creates an :class:`Introspector` instance suitable for use with the\n      given database.\n\n      .. code-block:: python\n\n         db = SqliteDatabase('my_app.db')\n         introspector = Introspector.from_database(db)\n         models = introspector.generate_models()\n\n         # User and Tweet (assumed to exist in the database) are\n         # peewee Model classes generated from the database schema.\n         User  = models['user']\n         Tweet = models['tweet']\n\n   .. method:: generate_models(skip_invalid=False, table_names=None, literal_column_names=False, bare_fields=False, include_views=False)\n\n      :param bool skip_invalid: Skip tables whose names are not valid Python\n          identifiers.\n      :param list table_names: Only generate models for the given tables.\n      :param bool literal_column_names: Use the exact database column names\n          as field names (rather than converting to Python naming conventions).\n      :param bool bare_fields: Do not attempt to detect field types; use\n          :class:`BareField` for all columns (**SQLite only**).\n      :param bool include_views: Also generate models for views.\n      :return: A dictionary mapping table-names to model classes.\n\n      Introspect the database, reading in the tables, columns, and foreign\n      key constraints, then generate a dictionary mapping each database table\n      to a dynamically-generated :class:`Model` class.\n\n\n.. _pwiz:\n\npwiz - Model Generator\n-----------------------\n\n.. module:: pwiz\n\n``pwiz`` is a command-line tool that introspects a database and prints\nready-to-use Peewee model code. If you have an existing database, running\n``pwiz`` saves significant time generating the initial model definitions.\n\n.. code-block:: shell\n\n   # Introspect a Postgresql database and write models to a file:\n   python -m pwiz -e postgresql -u postgres my_db > models.py\n\n   # Introspect a SQLite database:\n   python -m pwiz -e sqlite path/to/my.db\n\n   # Introspect a MySQL database (prompts for password):\n   python -m pwiz -e mysql -u root -P my_db\n\n   # Introspect only specific tables:\n   python -m pwiz -e postgresql my_db -t user,tweet,follow\n\n\nCommand-line options:\n\n+--------+-------------------------------------------+-------------------------+\n| Option | Meaning                                   | Example                 |\n+========+===========================================+=========================+\n| ``-e`` | Database backend                          | ``-e mysql``            |\n+--------+-------------------------------------------+-------------------------+\n| ``-H`` | Host                                      | ``-H 10.0.0.1``         |\n+--------+-------------------------------------------+-------------------------+\n| ``-p`` | Port                                      | ``-p 5432``             |\n+--------+-------------------------------------------+-------------------------+\n| ``-u`` | Username                                  | ``-u postgres``         |\n+--------+-------------------------------------------+-------------------------+\n| ``-P`` | Password (prompts interactively)          |                         |\n+--------+-------------------------------------------+-------------------------+\n| ``-s`` | Schema                                    | ``-s public``           |\n+--------+-------------------------------------------+-------------------------+\n| ``-t`` | Comma-separated list of tables to include | ``-t user,tweet``       |\n+--------+-------------------------------------------+-------------------------+\n| ``-v`` | Include views                             |                         |\n+--------+-------------------------------------------+-------------------------+\n| ``-i`` | Embed database info as a comment          |                         |\n+--------+-------------------------------------------+-------------------------+\n| ``-o`` | Preserve original column order            |                         |\n+--------+-------------------------------------------+-------------------------+\n| ``-I`` | Ignore fields whose type is unknown       |                         |\n+--------+-------------------------------------------+-------------------------+\n| ``-L`` | Use legacy table and column naming        |                         |\n+--------+-------------------------------------------+-------------------------+\n\nValid ``-e`` values: ``sqlite``, ``mysql``, ``postgresql``.\n\n.. warning::\n   If a password is required to access your database, you will be prompted to\n   enter it using a secure prompt.\n\n   **The password will be included in the output**. Specifically, at the top\n   of the file a :class:`Database` will be defined along with any required\n   parameters - including the password.\n\nExample output for a SQLite database with ``user`` and ``tweet`` tables:\n\n.. code-block:: python\n\n   from peewee import *\n\n   database = SqliteDatabase('example.db', **{})\n\n   class UnknownField(object):\n       def __init__(self, *_, **__): pass\n\n   class BaseModel(Model):\n       class Meta:\n           database = database\n\n   class User(BaseModel):\n       username = TextField(unique=True)\n\n       class Meta:\n           table_name = 'user'\n\n   class Tweet(BaseModel):\n       content = TextField()\n       timestamp = DateTimeField()\n       user = ForeignKeyField(column_name='user_id', field='id', model=User)\n\n       class Meta:\n           table_name = 'tweet'\n\nNote that ``pwiz`` detects foreign keys, unique constraints, and preserves\nexplicit table names.\n\n.. note::\n    The ``UnknownField`` is a placeholder that is used in the event your schema\n    contains a column declaration that Peewee doesn't know how to map to a\n    field class.\n\n.. _test-utils:\n\nTest Utilities\n--------------\n\n.. module:: playhouse.test_utils\n\n``playhouse.test_utils`` provides helpers for testing peewee projects.\n\n.. class:: count_queries(only_select=False)\n\n   Context manager that counts the number of SQL queries executed within\n   its block.\n\n   :param bool only_select: If ``True``, count only ``SELECT`` queries.\n\n   .. code-block:: python\n\n      with count_queries() as counter:\n          user = User.get(User.username == 'alice')\n          tweets = list(user.tweets)   # Triggers a second query.\n\n      assert counter.count == 2\n\n   .. attribute:: count\n\n      Number of queries executed.\n\n   .. method:: get_queries()\n\n      Return a list of ``(sql, params)`` 2-tuples for each query executed.\n\n.. function:: assert_query_count(expected, only_select=False)\n\n   Decorator or context manager that raises ``AssertionError`` if the number\n   of queries executed does not match ``expected``.\n\n   As a decorator:\n\n   .. code-block:: python\n\n       class TestAPI(unittest.TestCase):\n           @assert_query_count(1)\n           def test_get_user(self):\n               user = User.get_by_id(1)\n\n   As a context manager:\n\n   .. code-block:: python\n\n       with assert_query_count(3):\n           result = my_function_that_should_make_exactly_three_queries()\n"
  },
  {
    "path": "docs/peewee/example.rst",
    "content": ".. _example:\n\nExample app\n===========\n\nWe'll be building a simple *twitter*-like site. The source code for the example\ncan be found in the ``examples/twitter`` directory. You can also `browse the\nsource-code <https://github.com/coleifer/peewee/tree/master/examples/twitter>`__\non github.\n\n.. tip::\n   There is also an example `blog app <https://github.com/coleifer/peewee/tree/master/examples/blog>`__,\n   however it is not covered in this guide.\n\nThe example app uses the `flask <https://flask.palletsprojects.com/en/stable/>`__\nweb framework. You will need to install it to run the example:\n\n.. code-block:: shell\n\n   pip install flask\n\nRunning the Example\n-------------------\n\n.. image:: tweepee.png\n\nAfter ensuring that flask is installed, ``cd`` into the twitter example\ndirectory and execute the ``run_example.py`` script:\n\n.. code-block:: shell\n\n   python run_example.py\n\nThe example app will be accessible at http://localhost:5000/\n\nCode Structure\n--------------\n\nFor simplicity all example code is contained within a single module,\n``examples/twitter/app.py``. For a guide on structuring larger Flask apps with\npeewee, check out `Structuring Flask Apps <https://charlesleifer.com/blog/structuring-flask-apps-a-how-to-for-those-coming-from-django/>`_.\n\n.. _example-models:\n\nModels\n^^^^^^\n\npeewee uses declarative model definitions. Declare a model class for each\ntable. The model class then defines one or more field attributes which\ncorrespond to the table's columns. For the twitter clone, there are three\nmodels:\n\n**User**:\n    Represents a user account and stores the username and password, an email\n    address for generating avatars using *gravatar* and a datetime field\n    indicating when that account was created.\n\n**Relationship**:\n    This is a utility model that contains two foreign-keys to\n    the *User* model and stores which users follow one another.\n\n**Message**:\n    Analogous to a tweet. The Message model stores the text content of\n    the tweet, when it was created, and who posted it (foreign key to User).\n\nIf you like UML, these are the tables and relationships:\n\n.. image:: schema.jpg\n\nIn order to create these models we need:\n\n1. declare a :class:`SqliteDatabase` object\n2. declare our model classes\n3. declare columns as :class:`Field` instances on the model classes\n\n.. code-block:: python\n   :emphasize-lines: 3, 8, 13, 23, 39\n\n   # create a peewee database instance -- our models will use this database to\n   # persist information\n   database = SqliteDatabase(DATABASE)\n\n   # model definitions -- the standard \"pattern\" is to define a base model class\n   # that specifies which database to use.  then, any subclasses will automatically\n   # use the correct storage.\n   class BaseModel(Model):\n       class Meta:\n           database = database\n\n   # the user model specifies its fields (or columns) declaratively, like django\n   class User(BaseModel):\n       username = CharField(unique=True)\n       password = CharField()\n       email = CharField()\n       join_date = DateTimeField()\n\n   # this model contains two foreign keys to user -- it essentially allows us to\n   # model a \"many-to-many\" relationship between users.  by querying and joining\n   # on different columns we can expose who a user is \"related to\" and who is\n   # \"related to\" a given user\n   class Relationship(BaseModel):\n       from_user = ForeignKeyField(User, backref='relationships')\n       to_user = ForeignKeyField(User, backref='related_to')\n\n       class Meta:\n           # `indexes` is a tuple of 2-tuples, where the 2-tuples are\n           # a tuple of column names to index and a boolean indicating\n           # whether the index is unique or not.\n           indexes = (\n               # Specify a unique multi-column index on from/to-user.\n               (('from_user', 'to_user'), True),\n           )\n\n   # a dead simple one-to-many relationship: one user has 0..n messages, exposed by\n   # the foreign key. a users messages will be accessible as a special attribute,\n   # User.messages.\n   class Message(BaseModel):\n       user = ForeignKeyField(User, backref='messages')\n       content = TextField()\n       pub_date = DateTimeField()\n\n.. note::\n   Note that we create a *BaseModel* class that simply defines what database\n   we would like to use.  All other models then extend this class and will also\n   use the correct database connection.\n\n\nPeewee supports many different :ref:`field types <fields>` which map to\ndifferent column types commonly supported by database engines.  Conversion\nbetween python types and those used in the database is handled transparently,\nallowing you to use the following in your application:\n\n* Strings (unicode or otherwise)\n* Integers, floats, and ``Decimal`` numbers.\n* Boolean values\n* Dates, times and datetimes\n* ``None`` (NULL)\n* Binary data\n\nCreating Tables\n---------------\n\nIn order to start using the models, its necessary to **create the tables**.\nThis is a one-time operation and can be done quickly using the interactive\ninterpreter. We can create a small helper function to accomplish this:\n\n.. code-block:: python\n   :emphasize-lines: 3\n\n   def create_tables():\n       with database:\n           database.create_tables([User, Relationship, Message])\n\nOpen a python shell in the directory alongside the example app and execute the\nfollowing:\n\n.. code-block:: pycon\n\n    >>> from app import *\n    >>> create_tables()\n\n.. attention::\n   If you encounter an **ImportError** it means that either *flask* or *peewee*\n   was not found and may not be installed correctly. Check the :ref:`installation`\n   document for instructions on installing peewee.\n\nEvery model has a :meth:`~Model.create_table` classmethod which runs a SQL\n*CREATE TABLE* statement in the database. This method will create the table,\nincluding:\n\n* columns\n* foreign-key constraints\n* indexes\n* sequences\n* check constraints\n\nUsually this is something you'll only do once, when a new model is added.\n\nPeewee provides a helper method :meth:`Database.create_tables` which will\nresolve inter-model dependencies and call :meth:`~Model.create_table` on\neach model, ensuring the tables are created in order.\n\n.. note::\n   Adding, removing or modifying fields after the table has been created will\n   require you to either:\n\n   * drop the table and re-create it, OR\n   * manually add, drop or modify the columns, OR\n   * use the :ref:`migration tools <migrate>` to script your changes.\n\nDatabase Connection\n-------------------\n\nYou may have noticed in the above model code that there is a class defined on\nthe base model named *Meta* that sets the ``database`` attribute. Peewee allows\nevery model to specify which database it uses. There are many :ref:`Meta\noptions <model-options>` you can specify which control the behavior of your\nmodel.\n\nThis is a peewee idiom:\n\n.. code-block:: python\n   :emphasize-lines: 9, 10, 11\n\n   DATABASE = 'tweepee.db'\n\n   # Create a database instance that will manage the connection and\n   # execute queries\n   database = SqliteDatabase(DATABASE)\n\n   # Create a base-class all our models will inherit, which defines\n   # the database we'll be using.\n   class BaseModel(Model):\n       class Meta:\n           database = database\n\nWhen developing a web application, it's common to:\n\n1. Open a connection when a request starts.\n2. Run your request-handler.\n3. Close the connection before returning the response.\n\n**You should always manage your connections explicitly**. For instance, if you\nare using a :ref:`connection pool <pool>`, connections will only be recycled\ncorrectly if you call :meth:`~Database.connect` and :meth:`~Database.close`.\n\nFlask provides connection setup/teardown hooks via decorators:\n\n.. code-block:: python\n   :emphasize-lines: 1, 5\n\n   @app.before_request\n   def before_request():\n       database.connect()\n\n   @app.teardown_request\n   def teardown_request(exc):\n       if not database.is_closed():\n           database.close()\n\n.. seealso::\n   :ref:`framework-integration` covers setting-up hooks for a variety\n   of popular web frameworks.\n\n.. note::\n   Peewee uses thread local storage to manage connection state, so this\n   pattern can be used with multi-threaded or gevent WSGI servers.\n\n   Peewee's :ref:`asyncio integration <pwasyncio>` stores connection state in\n   task-local storage, so the same pattern applies.\n\nMaking Queries\n--------------\n\nIn the *User* model there are a few instance methods that encapsulate some\nuser-specific functionality:\n\n* ``following()``: who is this user following?\n* ``followers()``: who is following this user?\n\nThese methods are similar in their implementation but with an important\ndifference in the SQL *JOIN* and *WHERE* clauses:\n\n.. code-block:: python\n   :emphasize-lines: 4, 5, 11, 12\n\n   def following(self):\n       return (User\n               .select()\n               .join(Relationship, on=Relationship.to_user)\n               .where(Relationship.from_user == self)\n               .order_by(User.username))\n\n   def followers(self):\n       return (User\n               .select()\n               .join(Relationship, on=Relationship.from_user)\n               .where(Relationship.to_user == self)\n               .order_by(User.username))\n\nStoring Data\n------------\n\nWhen a new user wants to join the site we need to make sure the username is\navailable, and if so, create a new ``User`` record. Looking at the ``join()``\nview, we can see that our application attempts to create the ``User`` using\n:meth:`Model.create`. ``User.username`` field has a unique constraint, so if\nthe username is taken the database will raise an :class:`IntegrityError`.\n\n.. code-block:: python\n\n   try:\n       with database.atomic():\n           # Attempt to create the user. If the username is taken, due to the\n           # unique constraint, the database will raise an IntegrityError.\n           user = User.create(\n               username=request.form['username'],\n               password=md5(request.form['password']).hexdigest(),\n               email=request.form['email'],\n               join_date=datetime.datetime.now())\n\n       # mark the user as being 'authenticated' by setting the session vars\n       auth_user(user)\n       return redirect(url_for('homepage'))\n\n   except IntegrityError:\n       flash('That username is already taken')\n\nWe will use a similar approach when a user wishes to follow someone. To\nindicate a following relationship, we create a row in the ``Relationship``\ntable pointing from one user to another. Due to the unique index on\n``(from_user, to_user)``, we will be sure not to end up with duplicate rows:\n\n.. code-block:: python\n\n   user = get_object_or_404(User, username=username)\n   try:\n       with database.atomic():\n           Relationship.create(\n               from_user=get_current_user(),\n               to_user=user)\n   except IntegrityError:\n       pass\n\nSubqueries\n----------\n\nIf you are logged-in and visit the twitter homepage, you will see tweets from\nthe users that you follow. In order to implement this cleanly, we can use a\nsubquery:\n\n.. note::\n   ``user.following()`` will automatically only select ``User.id`` when it used\n   in a subquery.\n\n.. code-block:: python\n   :emphasize-lines: 5\n\n   # python code\n   user = get_current_user()\n   messages = (Message\n               .select()\n               .where(Message.user.in_(user.following()))\n               .order_by(Message.pub_date.desc()))\n\nThis code corresponds to the following SQL query:\n\n.. code-block:: sql\n\n   SELECT t1.\"id\", t1.\"user_id\", t1.\"content\", t1.\"pub_date\"\n   FROM \"message\" AS t1\n   WHERE t1.\"user_id\" IN (\n       SELECT t2.\"id\"\n       FROM \"user\" AS t2\n       INNER JOIN \"relationship\" AS t3\n           ON t2.\"id\" = t3.\"to_user_id\"\n       WHERE t3.\"from_user_id\" = ?\n   )\n\nOther Topics\n------------\n\nThere are a couple other neat things going on in the example app that are worth\nmentioning briefly.\n\n* Support for paginating lists of results is implemented in a simple function called\n  ``object_list``. This function is used by all the views that return lists of objects.\n\n  .. code-block:: python\n\n     def object_list(template_name, qr, var_name='object_list', **kwargs):\n         kwargs.update(\n             page=int(request.args.get('page', 1)),\n             pages=qr.count() / 20 + 1)\n         kwargs[var_name] = qr.paginate(kwargs['page'])\n         return render_template(template_name, **kwargs)\n\n* Simple authentication system with a ``login_required`` decorator. The first\n  function simply adds user data into the current session when a user successfully\n  logs in. The decorator ``login_required`` can be used to wrap view functions,\n  checking for whether the session is authenticated and if not redirecting to the\n  login page.\n\n  .. code-block:: python\n\n     def auth_user(user):\n         session['logged_in'] = True\n         session['user'] = user\n         session['username'] = user.username\n         flash('You are logged in as %s' % (user.username))\n\n     def login_required(f):\n         @wraps(f)\n         def inner(*args, **kwargs):\n             if not session.get('logged_in'):\n                 return redirect(url_for('login'))\n             return f(*args, **kwargs)\n         return inner\n\n* Return a 404 response instead of throwing exceptions when an object is not\n  found in the database.\n\n  .. code-block:: python\n\n     def get_object_or_404(model, *expressions):\n         try:\n             return model.get(*expressions)\n         except model.DoesNotExist:\n             abort(404)\n\n.. tip::\n   To avoid having to frequently copy/paste :func:`object_list` or\n   :func:`get_object_or_404`, these functions are included as part of the\n   playhouse :ref:`flask extension module <flask-utils>`.\n\n   .. code-block:: python\n\n      from playhouse.flask_utils import get_object_or_404, object_list\n\nMore Examples\n-------------\n\nThere are more examples included in the peewee `examples directory\n<https://github.com/coleifer/peewee/blob/master/examples/>`_, including:\n\n* `Example blog app <https://github.com/coleifer/peewee/tree/master/examples/blog>`__ using Flask and peewee. Also see `accompanying blog post <https://charlesleifer.com/blog/how-to-make-a-flask-blog-in-one-hour-or-less/>`__.\n* `An encrypted command-line diary <https://github.com/coleifer/peewee/blob/master/examples/diary.py>`_. There is a `companion blog post <https://charlesleifer.com/blog/dear-diary-an-encrypted-command-line-diary-with-python/>`__ you might enjoy as well.\n* `Analytics web-service <https://github.com/coleifer/peewee/tree/master/examples/analytics>`_ (like a lite version of Google Analytics). Also check out the `companion blog post <https://charlesleifer.com/blog/saturday-morning-hacks-building-an-analytics-app-with-flask/>`__.\n\n.. seealso::\n   Like these snippets and interested in more?  Check out `flask-peewee <https://github.com/coleifer/flask-peewee>`__ -\n   a flask plugin that provides a django-like Admin interface, RESTful API, Authentication and\n   more for your peewee models.\n"
  },
  {
    "path": "docs/peewee/framework_integration.rst",
    "content": ".. _framework-integration:\n\nFramework Integration\n=====================\n\nFor web applications, it is common to open a connection when a request is\nreceived, and to close the connection when the response is delivered. This\ndocument describes how to add hooks to your web app to ensure the database\nconnection is handled properly.\n\nThese steps will ensure that regardless of whether you're using a simple\n:class:`SqliteDatabase` or a :class:`PooledPostgresqlDatabase`, peewee will\nhandle the connections correctly.\n\nThe pattern is always the same:\n\n.. code-block:: python\n\n   # On request start:\n   db.connect()\n\n   # On request end (success or error):\n   if not db.is_closed():\n       db.close()\n\nEvery framework exposes hooks for this. The sections below show the idiomatic\napproach for each.\n\n.. note::\n   Applications that handle significant traffic should use a\n   :ref:`connection pool <connection-pooling>` to avoid the overhead of\n   establishing a new connection per request. Pooled databases can be used as\n   drop-in replacements for their non-pooled counterparts.\n\n.. _flask:\n\nFlask\n-----\n\nFor a **complete** Flask + Peewee application example, including authentication\nand other common webapp functionality, see :ref:`example`. There is also a\nfull `blog app <https://github.com/coleifer/peewee/tree/master/examples/blog>`__\nand an `analytics app <https://github.com/coleifer/peewee/tree/master/examples/analytics>`__\nin the project ``examples/`` directory.\n\nThe **minimal** Flask integration ensures that database connection lifecycles\nare tied to the request/response cycle via ``before_request`` and ``teardown_request``\nhooks:\n\n.. code-block:: python\n\n   from flask import Flask\n   from peewee import *\n\n   db = SqliteDatabase('my_app.db')\n   app = Flask(__name__)\n\n   @app.before_request\n   def _db_connect():\n       db.connect()\n\n   @app.teardown_request\n   def _db_close(exc):\n       if not db.is_closed():\n           db.close()\n\n``teardown_request`` is called regardless of whether the request succeeded or\nraised an exception, making it the correct hook for cleanup.\n\nFor applications that receive a large number of requests, a connection pool is\nrecommended:\n\n.. code-block:: python\n\n   from flask import Flask\n   from playhouse.pool import PooledPostgresqlDatabase\n\n   db = PooledPostgresqlDatabase('app', host='10.8.0.1', user='postgres')\n   app = Flask(__name__)\n\n   # Note that when using the pooled implementation the hooks are the exact\n   # same. Opening and closing the connection simply acquires and releases it\n   # from the pool for the lifetime of the request.\n   @app.before_request\n   def _db_connect():\n       db.connect()\n\n   @app.teardown_request\n   def _db_close(exc):\n       if not db.is_closed():\n           db.close()\n\n.. seealso::\n   The :ref:`flask-utils` extension provides helpers for common tasks like\n   declarative database configuration, object retrieval and pagination.\n\n.. _fastapi:\n\nFastAPI\n-------\n\nFastAPI is an async framework and can be used with Peewee's :ref:`pwasyncio`\nintegration or synchronously. Peewee also provides :ref:`pydantic` support,\nwhich works well with FastAPI.\n\nQuick note on SQLModel\n^^^^^^^^^^^^^^^^^^^^^^\n\nFastAPI advocates using SQLModel for database access. SQLModel combines\nSQLAlchemy and Pydantic into a single class, which may work well for simple\nexamples. There are a few things to watch out for, though:\n\n* SQLModel's official tutorial uses synchronous endpoints exclusively, which\n  FastAPI runs on a threadpool. Async usage is listed as an \"advanced\" topic\n  and is undocumented currently.\n* Lazy-loading often breaks in async contexts. SQLAlchemy's implicit lazy-loading\n  of relationships can trigger ``MissingGreenlet`` errors when used with async\n  sessions. This can also occur with Peewee, but it's straightforward to avoid\n  by selecting joined relations.\n* Because SQLModel uses synchronous drivers for DDL and certain operations, you\n  typically need both a sync AND async driver installed, along with separate\n  engine configurations.\n* SQLModel uses inheritance to manage input, output and table schemas. In\n  practice a single database table often requires three or four model classes,\n  e.g. ``UserBase``, ``User``, ``UserCreate`` and ``UserRead`` - all of this is\n  managed through inheritance.\n\nPeewee may provide a simpler experience - there is a single database to manage\nwith built-in pooling, fewer implicit lazy-load gotcha's, and the Pydantic\nschemas generated with :func:`~playhouse.pydantic_utils.to_pydantic` can be\nconfigured to include/exclude fields without inheritance. Field metadata is\ncaptured automatically: choice enums, default values, descriptions, titles and\ntype information are captured in the JSON schema and OpenAPI docs.\n\nPeewee requires far less machinery to provide real asyncio database access, and\nof course works equally well for synchronous FastAPI endpoints.\n\nAsync Example using Pydantic\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nBelow is a full example FastAPI application demonstrating dependency-injection\nstyle hooks, fully :ref:`async query execution <pwasyncio>`, and\n:ref:`pydantic integration <pydantic>`:\n\n.. code-block:: python\n\n   # example.py\n   from fastapi import Depends, FastAPI, HTTPException\n   from contextlib import asynccontextmanager\n   from peewee import *\n   from playhouse.pwasyncio import AsyncPostgresqlDatabase\n   from playhouse.pydantic_utils import to_pydantic\n\n\n   db = AsyncPostgresqlDatabase('peewee_test')\n\n   class User(Model):\n       name = CharField(verbose_name='Full Name', help_text='Display name')\n       email = CharField(unique=True)\n       status = IntegerField(default=1, choices=(\n           (1, 'Active'),\n           (2, 'Inactive'),\n           (3, 'Deleted')))\n       class Meta:\n           database = db\n\n   # Generate pydantic schemas suitable for create and responses.\n   # Schemas will include metadata from verbose_name, help_text, choices, and\n   # default settings.\n   UserCreate = to_pydantic(User, model_name='UserCreate')\n   UserResponse = to_pydantic(User, exclude_autofield=False, model_name='UserResponse')\n\n   async def get_db():\n       async with db:\n           yield db\n\n   @asynccontextmanager\n   async def lifespan(app):\n       # Create tables (if they don't exist) at application startup.\n       async with db:\n           await db.acreate_tables([User])\n       yield\n       await db.close_pool()  # Shut-down pool and exit.\n\n   app = FastAPI(lifespan=lifespan)\n\n   @app.get('/users', response_model=list[UserResponse])\n   async def list_users(db=Depends(get_db)):\n       rows = await db.list(User.select().dicts())\n       return [UserResponse(**row) for row in rows]\n\n   @app.post('/users', response_model=UserResponse)\n   async def create_user(data: UserCreate, db=Depends(get_db)):\n       user = await db.run(User.create, **data.model_dump())\n       return UserResponse.model_validate(user)\n\n   @app.get('/users/{user_id}', response_model=UserResponse)\n   async def get_user(user_id: int, db=Depends(get_db)):\n       try:\n           user = await db.get(User.select().where(User.id == user_id))\n       except User.DoesNotExist:\n           raise HTTPException(status_code=404, detail='User not found')\n       return UserResponse.model_validate(user)\n\nRun the example:\n\n.. code-block:: console\n\n   $ fastapi dev example.py\n\nPopulate and query data:\n\n.. code-block:: console\n\n   $ curl -X POST http://localhost:8000/users \\\n        -H \"Content-Type: application/json\" \\\n        -d '{\"name\": \"Alice\", \"email\": \"alice@example.com\"}'\n\n   {\"id\":1,\"name\":\"Alice\",\"email\":\"alice@example.com\",\"status\":1}\n\n   $ curl -X POST http://localhost:8000/users \\\n        -H \"Content-Type: application/json\" \\\n        -d '{\"name\": \"Bob\", \"email\": \"bob@example.com\", \"status\": 2}'\n\n   {\"id\":2,\"name\":\"Bob\",\"email\":\"bob@example.com\",\"status\":2}\n\n   $ curl http://localhost:8000/users\n\n   [{\"id\":1,\"name\":\"Alice\",\"email\":\"alice@example.com\",\"status\":1},\n    {\"id\":2,\"name\":\"Bob\",\"email\":\"bob@example.com\",\"status\":2}]\n\n   $ curl http://localhost:8000/users/1\n\n   {\"id\":1,\"name\":\"Alice\",\"email\":\"alice@example.com\",\"status\":1}\n\nWe can also verify that the pydantic schemas captured our Peewee model\nmetadata:\n\n.. code-block:: python\n\n   >>> UserCreate.model_json_schema()\n   {'properties': {\n      'name': {\n         'description': 'Display name',\n         'title': 'Full Name',\n         'type': 'string'},\n      'email': {\n         'title': 'Email',\n         'type': 'string'},\n     'status': {\n         'default': 1,\n         'description': 'Choices: 1 = Active, 2 = Inactive, 3 = Deleted',\n         'enum': [1, 2, 3],\n         'title': 'Status',\n         'type': 'integer'}},\n    'required': ['name', 'email'],\n    'title': 'UserCreate',\n    'type': 'object'}\n\n.. seealso::\n   * :ref:`pwasyncio`\n   * :ref:`pydantic`\n\nDependency injection\n^^^^^^^^^^^^^^^^^^^^^\n\nThe following is a minimal example demonstrating:\n\n* Ensure connection is opened and closed automatically for endpoints that use\n  the database.\n* Create tables/resources when app server starts.\n* Shut-down connection pool when app server exits.\n\n.. code-block:: python\n\n   from contextlib import asynccontextmanager\n   from fastapi import Depends, FastAPI\n   from peewee import *\n   from playhouse.pwasyncio import *\n\n\n   app = FastAPI()\n\n   db = AsyncPostgresqlDatabase('peewee_test', host='10.8.0.1', user='postgres')\n\n   async def get_db():\n       async with db:\n           yield db\n\n   @asynccontextmanager\n   async def lifespan(app):\n       async with db:\n           await db.acreate_tables([User])\n       yield\n       await db.close_pool()\n\n   app = FastAPI(lifespan=lifespan)\n\n   @app.get('/users')\n   async def list_users(db=Depends(get_db)):\n       return await db.list(User.select().dicts())\n\nMiddleware and startup hooks\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe following example demonstrates how to use middleware and startup hooks\ninstead of dependency injection.\n\n* Ensure connection is opened and closed for each request.\n* Create tables/resources when app server starts.\n* Shut-down connection pool when app server exits.\n\n.. code-block:: python\n\n   from fastapi import FastAPI\n   from peewee import *\n   from playhouse.pwasyncio import *\n\n\n   app = FastAPI()\n\n   db = AsyncPostgresqlDatabase('peewee_test', host='10.8.0.1', user='postgres')\n\n   @app.middleware('http')\n   async def database_connection(request, call_next):\n       async with db:  # Obtain connection from connection pool.\n           response = await call_next(request)\n       return response\n\n   @app.on_event('startup')\n   async def on_startup():\n       async with db:\n           await db.acreate_tables([Model1, Model2, Model3, ...])\n\n   @app.on_event('shutdown')\n   async def on_shutdown():\n       await db.close_pool()\n\n   # Async queries.\n   @app.get('/users')\n   async def list_users():\n       return await db.list(User.select().dicts())\n\n   @app.post('/users')\n   async def create_user(name: str):\n       user = await db.run(User.create, name=name)\n       return {'id': user.id, 'name': user.name}\n\nSynchronous FastAPI\n^^^^^^^^^^^^^^^^^^^\n\nIf you are using synchronous endpoints with FastAPI, you can use the\nsynchronous Peewee database implementations. Here is the above \"Full Example\"\nimplemented using sync Peewee:\n\n.. code-block:: python\n\n   from fastapi import Depends, FastAPI, HTTPException\n   from contextlib import asynccontextmanager\n   from peewee import *\n   from playhouse.pydantic_utils import to_pydantic\n\n   db = PostgresqlDatabase('peewee_test')\n\n   class User(Model):\n       name = CharField(verbose_name='Full Name', help_text='Display name')\n       email = CharField(unique=True)\n       status = IntegerField(default=1, choices=(\n           (1, 'Active'),\n           (2, 'Inactive'),\n           (3, 'Deleted')))\n\n       class Meta:\n           database = db\n\n   # Generate pydantic schemas suitable for create and responses.\n   UserCreate = to_pydantic(User, model_name='UserCreate')\n   UserResponse = to_pydantic(User, exclude_autofield=False, model_name='UserResponse')\n\n   def get_db():\n       with db.connection_context():\n           yield db\n\n   @asynccontextmanager\n   async def lifespan(app):\n       with db:\n           db.create_tables([User])\n       yield\n\n   app = FastAPI(lifespan=lifespan)\n\n   @app.get('/users', response_model=list[UserResponse])\n   def list_users(database=Depends(get_db)):\n       rows = User.select().dicts()\n       return [UserResponse(**row) for row in rows]\n\n   @app.post('/users', response_model=UserResponse)\n   def create_user(data: UserCreate, database=Depends(get_db)):\n       user = User.create(**data.model_dump())\n       return UserResponse.model_validate(user)\n\n   @app.get('/users/{user_id}', response_model=UserResponse)\n   def get_user(user_id: int, database=Depends(get_db)):\n       try:\n           user = User.get(User.id == user_id)\n       except User.DoesNotExist:\n           raise HTTPException(status_code=404, detail='User not found')\n       return UserResponse.model_validate(user)\n\n.. seealso:: :ref:`pydantic`\n\nDjango\n------\n\nAdd a middleware that opens the connection before the view runs and closes it\nafter the response is prepared. Place it first in ``MIDDLEWARE`` so it wraps\nall other middleware:\n\n.. code-block:: python\n\n   # myproject/middleware.py\n   from myproject.db import database\n\n   def PeeweeConnectionMiddleware(get_response):\n       def middleware(request):\n           database.connect()\n           try:\n               response = get_response(request)\n           finally:\n               if not database.is_closed():\n                   database.close()\n           return response\n       return middleware\n\n.. code-block:: python\n\n   # settings.py\n   MIDDLEWARE = [\n       'myproject.middleware.PeeweeConnectionMiddleware',\n       # ... rest of middleware ...\n   ]\n\nBottle\n------\n\nUse the ``before_request`` and ``after_request`` hooks:\n\n.. code-block:: python\n\n   from bottle import hook\n   from peewee import *\n\n   db = SqliteDatabase('my_app.db')\n\n   @hook('before_request')\n   def _connect_db():\n       db.connect()\n\n   @hook('after_request')\n   def _close_db():\n       if not db.is_closed():\n           db.close()\n\nFalcon\n------\n\nAdd a middleware component:\n\n.. code-block:: python\n\n   import falcon\n   from peewee import *\n\n   db = SqliteDatabase('my_app.db')\n\n   class DatabaseMiddleware:\n       def process_request(self, req, resp):\n           db.connect()\n\n       def process_response(self, req, resp, resource, req_succeeded):\n           if not db.is_closed():\n               db.close()\n\n   app = falcon.App(middleware=[DatabaseMiddleware()])\n\n\nPyramid\n-------\n\nSet up a custom ``Request`` factory:\n\n.. code-block:: python\n\n   from pyramid.request import Request\n   from peewee import *\n\n   db = SqliteDatabase('my_app.db')\n\n   class MyRequest(Request):\n       def __init__(self, *args, **kwargs):\n           super().__init__(*args, **kwargs)\n           db.connect()\n           self.add_finished_callback(self._close_db)\n\n       def _close_db(self, request):\n           if not db.is_closed():\n               db.close()\n\n   # In your application factory:\n   def main(global_settings, **settings):\n       config = Configurator(settings=settings)\n       config.set_request_factory(MyRequest)\n\nSanic\n-----\n\nSanic is an async framework and can be used with Peewee's :ref:`pwasyncio`\nintegration.\n\n.. code-block:: python\n\n   from sanic import Sanic\n   from peewee import *\n   from playhouse.pwasyncio import *\n\n\n   app = Sanic('PeeweeApp')\n\n   db = AsyncPostgresqlDatabase('peewee_test', host='10.8.0.1', user='postgres')\n\n   @app.on_request\n   async def open_connection(request):\n       await db.aconnect()  # Obtain connection from connection pool.\n\n   @app.on_response\n   async def close_connection(request, response):\n       await db.aclose()  # Return connection to pool.\n\n   @app.before_server_start\n   async def setup_db(app):\n       async with db:\n           await db.acreate_tables([Model1, Model2, Model3, ...])\n\n   @app.before_server_stop\n   async def shutdown_db(app):\n       await db.close_pool()\n\nExample demonstrating executing an async query:\n\n.. code-block:: python\n\n   from sanic import json\n\n   @app.get('/message/')\n   async def message(request):\n       # Get the latest message from the database.\n       message = await db.get(Message.select().order_by(Message.id.desc()))\n       return json({'content': message.content, 'id': message.id})\n\n.. seealso:: :ref:`pwasyncio`\n\nCherryPy\n--------\n\nSubscribe to the engine's before/after request events:\n\n.. code-block:: python\n\n   import cherrypy\n   from peewee import *\n\n   db = SqliteDatabase('my_app.db')\n\n   def _db_connect():\n       db.connect()\n\n   def _db_close():\n       if not db.is_closed():\n           db.close()\n\n   cherrypy.engine.subscribe('before_request', _db_connect)\n   cherrypy.engine.subscribe('after_request', _db_close)\n\nGeneral Pattern for Any Framework\n---------------------------------\n\nIf your framework is not listed here, the integration follows the same\nstructure:\n\n1. Find the hook that runs before every request handler.\n2. Call ``db.connect()`` there.\n3. Find the hook that runs after every request (success and error both).\n4. Call ``db.close()`` there if the connection is open.\n\nAny WSGI or ASGI middleware that wraps the application callable can also\nmanage this:\n\n.. code-block:: python\n\n   class PeeweeMiddleware:\n       def __init__(self, app, database):\n           self.app = app\n           self.db = database\n\n       def __call__(self, environ, start_response):\n           self.db.connect()\n           try:\n               return self.app(environ, start_response)\n           finally:\n               if not self.db.is_closed():\n                   self.db.close()\n\n   # Wrap your WSGI app:\n   application = PeeweeMiddleware(application, db)\n"
  },
  {
    "path": "docs/peewee/installation.rst",
    "content": ".. _installation:\n\nInstalling and Testing\n======================\n\nInstall the latest release from PyPI:\n\n.. code-block:: shell\n\n   pip install peewee\n\nPeewee has an optional Sqlite C extension which is not bundled in the default\nwheel. It provides user-defined ranking functions for use with Sqlite FTS4 and\nfunctions for fuzzy string matching. To build from source:\n\n.. code-block:: shell\n\n   pip install peewee --no-binary :all:\n\nInstalling from Source\n----------------------\n\n.. code-block:: shell\n\n   git clone https://github.com/coleifer/peewee.git\n   cd peewee\n   pip install .\n\nRunning Tests\n-------------\n\n.. code-block:: shell\n\n   python runtests.py\n   python runtests.py --help  # Show options.\n\nTo run tests against Postgres or MySQL create a database named ``peewee_test``.\nFor the Postgres extension tests, enable hstore:\n\n.. code-block:: sql\n\n   CREATE EXTENSION hstore;\n\nSupported Drivers\n-----------------\n\nPeewee works with any database for which a DB-API 2.0 driver exists. The\nfollowing drivers are supported out of the box:\n\n+-----------------------+------------------------+--------------------------------------------+\n| Database              | Driver                 | Implementation                             |\n+=======================+========================+============================================+\n| **Sqlite**            | ``sqlite3``            | :class:`SqliteDatabase`                    |\n+-----------------------+------------------------+--------------------------------------------+\n| **Postgres**          | ``psycopg3``           | :class:`PostgresqlDatabase`                |\n+-----------------------+------------------------+--------------------------------------------+\n| **Postgres**          | ``psycopg2``           | :class:`PostgresqlDatabase`                |\n+-----------------------+------------------------+--------------------------------------------+\n| **MySQL**             | ``pymysql``            | :class:`MySQLDatabase`                     |\n+-----------------------+------------------------+--------------------------------------------+\n| Sqlite (async)        | ``aiosqlite``          | :class:`.AsyncSqliteDatabase`              |\n+-----------------------+------------------------+--------------------------------------------+\n| Postgres (async)      | ``asyncpg``            | :class:`.AsyncPostgresqlDatabase`          |\n+-----------------------+------------------------+--------------------------------------------+\n| MySQL (async)         | ``aiomysql``           | :class:`.AsyncMySQLDatabase`               |\n+-----------------------+------------------------+--------------------------------------------+\n| Sqlite (alternate)    | ``cysqlite``           | :class:`.CySqliteDatabase`                 |\n+-----------------------+------------------------+--------------------------------------------+\n| Sqlite (alternate)    | ``apsw``               | :class:`.APSWDatabase`                     |\n+-----------------------+------------------------+--------------------------------------------+\n| SqlCipher             | ``sqlcipher3``         | :class:`.SqlCipherDatabase`                |\n+-----------------------+------------------------+--------------------------------------------+\n| MySQL (alternate)     | ``mysql-connector``    | :class:`.MySQLConnectorDatabase`           |\n+-----------------------+------------------------+--------------------------------------------+\n| MariaDB (alternate)   | ``mariadb-connector``  | :class:`.MariaDBConnectorDatabase`         |\n+-----------------------+------------------------+--------------------------------------------+\n| CockroachDB           | ``psycopg`` (2 or 3)   | :class:`.CockroachDatabase`                |\n+-----------------------+------------------------+--------------------------------------------+\n| Postgres (extensions) | ``psycopg`` (2 or 3)   | :class:`.PostgresqlExtDatabase`            |\n+-----------------------+------------------------+--------------------------------------------+\n\nThe three bolded rows cover the majority of deployments. All others are\noptional; install their drivers when needed.\n"
  },
  {
    "path": "docs/peewee/interactive.rst",
    "content": ".. _interactive:\n\nUsing Peewee Interactively\n==========================\n\nPeewee contains helpers for working interactively from a Python interpreter or\nsomething like a Jupyter notebook. For this example, we'll assume that we have\na pre-existing Sqlite database with the following simple schema:\n\n.. code-block:: sql\n\n   CREATE TABLE IF NOT EXISTS \"event\" (\n       \"id\" INTEGER NOT NULL PRIMARY KEY,\n       \"key\" TEXT NOT NULL,\n       \"timestamp\" DATETIME NOT NULL,\n       \"metadata\" TEXT NOT NULL);\n\nTo experiment with querying this database from an interactive interpreter\nsession, we would start our interpreter and import the following helpers:\n\n* :class:`SqliteDatabase` - to reference the \"events.db\"\n* :func:`playhouse.reflection.generate_models` - to generate models from an\n  existing database.\n* :func:`playhouse.reflection.print_model` - to view the model definition.\n* :func:`playhouse.reflection.print_table_sql` - to view the table SQL.\n\nOur terminal session might look like this:\n\n.. code-block:: pycon\n\n   >>> from peewee import SqliteDatabase\n   >>> from playhouse.reflection import generate_models, print_model, print_table_sql\n   >>>\n\nThe :func:`~playhouse.reflection.generate_models` function will introspect the database and\ngenerate model classes for all the tables that are found. This is a handy way\nto get started and can save a lot of typing. The function returns a dictionary\nkeyed by the table name, with the generated model as the corresponding value:\n\n.. code-block:: pycon\n\n   >>> db = SqliteDatabase('events.db')\n   >>> models = generate_models(db)\n   >>> list(models.items())\n   [('events', <Model: event>)]\n\n   >>> globals().update(models)  # Inject models into global namespace.\n   >>> event\n   <Model: event>\n\nTo take a look at the model definition, which lists the model's fields and\ndata-type, we can use the :func:`~playhouse.reflection.print_model` function:\n\n.. code-block:: pycon\n\n   >>> print_model(event)\n   event\n     id AUTO\n     key TEXT\n     timestamp DATETIME\n     metadata TEXT\n\nWe can also generate a SQL ``CREATE TABLE`` for the introspected model, if you\nfind that easier to read. This should match the actual table definition in the\nintrospected database:\n\n.. code-block:: pycon\n\n   >>> print_table_sql(event)\n   CREATE TABLE IF NOT EXISTS \"event\" (\n     \"id\" INTEGER NOT NULL PRIMARY KEY,\n     \"key\" TEXT NOT NULL,\n     \"timestamp\" DATETIME NOT NULL,\n     \"metadata\" TEXT NOT NULL)\n\nNow that we are familiar with the structure of the table we're working with, we\ncan run some queries on the generated ``event`` model:\n\n.. code-block:: pycon\n\n   >>> for e in event.select().order_by(event.timestamp).limit(5):\n   ...     print(e.key, e.timestamp)\n   ...\n   e00 2019-01-01 00:01:00\n   e01 2019-01-01 00:02:00\n   e02 2019-01-01 00:03:00\n   e03 2019-01-01 00:04:00\n   e04 2019-01-01 00:05:00\n\n   >>> event.select(fn.MIN(event.timestamp), fn.MAX(event.timestamp)).scalar(as_tuple=True)\n   (datetime.datetime(2019, 1, 1, 0, 1), datetime.datetime(2019, 1, 1, 1, 0))\n\n   >>> event.select().count()  # Or, len(event)\n   60\n\nFor more information about these APIs and other similar reflection utilities,\nsee the :ref:`reflection` documentation.\n\nTo generate an actual Python module containing model definitions for an\nexisting database, you can use the command-line :ref:`pwiz <pwiz>` tool. Here\nis a quick example:\n\n.. code-block:: shell\n\n   pwiz -e sqlite events.db > events.py\n\nThe ``events.py`` file will now be an import-able module containing a database\ninstance (referencing the ``events.db``) along with model definitions for any\ntables found in the database. ``pwiz`` does some additional nice things like\nintrospecting indexes and adding proper flags for ``NULL``/``NOT NULL``\nconstraints, etc.\n\nThe APIs discussed in this section:\n\n* :func:`~playhouse.reflection.generate_models`\n* :func:`~playhouse.reflection.print_model`\n* :func:`~playhouse.reflection.print_table_sql`\n\nMore low-level APIs are also available on the :class:`Database` instance:\n\n* :meth:`Database.get_tables`\n* :meth:`Database.get_indexes`\n* :meth:`Database.get_columns` (for a given table)\n* :meth:`Database.get_primary_keys` (for a given table)\n* :meth:`Database.get_foreign_keys` (for a given table)\n"
  },
  {
    "path": "docs/peewee/models.rst",
    "content": ".. _models:\n\nModels and Fields\n=================\n\nModels and Fields allow Peewee applications to declare the tables and columns\nthey will use, and issue queries using Python. This document explains how to\nuse Peewee to express database tables and columns.\n\n:class:`Model` classes, :class:`Field` instances and model instances all\nmap to database concepts:\n\n================= =================================\nPython construct  Database concept\n================= =================================\nModel class       Table\nField instance    Column\nModel instance    Row\n================= =================================\n\n.. tip::\n   If you are connecting Peewee to an existing database rather than defining a\n   schema from scratch, the :ref:`pwiz <pwiz>` tool can generate model\n   definitions automatically by introspecting the database.\n\nThe following code shows the typical way you will define your database\nconnection and model classes.\n\n.. code-block:: python\n   :emphasize-lines: 4, 6, 10\n\n   import datetime\n   from peewee import *\n\n   db = SqliteDatabase('my_app.db')\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = CharField(unique=True)\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(default=datetime.datetime.now)\n       is_published = BooleanField(default=True)\n\n   class Favorite(BaseModel):\n       user = ForeignKeyField(User, backref='favorites')\n       tweet = ForeignKeyField(Tweet, backref='favorites')\n\nThree things to note:\n\n1. Create an instance of a :class:`Database`.\n\n   .. code-block:: python\n\n      db = SqliteDatabase('my_app.db')\n\n   The ``db`` object will be used to manage the connections to the Sqlite\n   database. In this example we're using :class:`SqliteDatabase`, but you\n   could also use one of the other :ref:`database engines <database>`.\n\n2. Create a base model class which specifies our database.\n\n   .. code-block:: python\n\n      class BaseModel(Model):\n          class Meta:\n              database = db\n\n   **BaseModel** exists only to specify the ``database`` setting in its ``Meta``\n   class. Because ``Meta.database`` is inheritable, every model that extends\n   ``BaseModel`` will automatically use the same database. This pattern avoids\n   repeating the database assignment on every model class.\n\n   Model configuration is kept namespaced in a special class called ``Meta``.\n   :ref:`Meta <model-options>` configuration is passed on to subclasses, so\n   our project's models will all subclass *BaseModel*. There are\n   :ref:`many different attributes <model-options>` you can configure using *Model.Meta*.\n\n3. Declare model classes and fields.\n\n   .. code-block:: python\n\n      class User(BaseModel):\n          username = CharField(unique=True)\n\n   Model definition uses the declarative style seen in other popular ORMs.\n   Note that we are extending the *BaseModel* class so the *User* model will\n   inherit the database connection.\n\n   We have explicitly defined a single *username* column with a unique\n   constraint. Because we have not specified a primary key, Peewee will\n   automatically add an auto-incrementing integer primary key field named\n   *id*.\n\nModel Inheritance\n-----------------\n\nModel subclasses inherit the ``Meta`` configuration of their parent as well as\nthe parent's fields. Inherited ``Meta`` attributes (such as ``database``) are\nshared; non-inheritable attributes (such as ``table_name``) are re-derived for\neach subclass.\n\n.. code-block:: python\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\n   class TimestampedModel(BaseModel):\n       \"\"\"Adds created/updated timestamps to any subclass.\"\"\"\n       created = DateTimeField(default=datetime.datetime.now)\n       updated = DateTimeField(default=datetime.datetime.now)\n\n   class Article(TimestampedModel):\n       title = TextField()\n       body = TextField()\n       # Article.created and Article.updated are inherited.\n       # Article._meta.database is inherited from BaseModel.\n\nPeewee uses a separate table for each concrete model class. There is no\nnotion of inheritance spanning multiple tables. If you subclass a model,\nboth the parent and the child have their own tables.\n\n.. _fields:\n\nFields\n------\n\nThe :class:`Field` class is used to describe the mapping of :class:`Model`\nattributes to database columns. Each field type has a corresponding SQL storage\nclass (varchar, int, etc). Fields handle conversion between python data types\nand underlying storage transparently.\n\nWhen creating a :class:`Model` class, fields are defined as class attributes:\n\n.. code-block:: python\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(default=datetime.datetime.now)\n       is_published = BooleanField(default=True)\n\nIn the above example, no field specifies ``primary_key=True``. As a result,\nPeewee will create an auto-incrementing integer primary key named ``id``.\nPeewee uses :class:`AutoField` to signify an auto-incrementing integer primary\nkey.\n\n.. _field_types_table:\n\nField types\n^^^^^^^^^^^\n\n=====================   =================   =================   =================\nField Type              Sqlite              Postgresql          MySQL\n=====================   =================   =================   =================\n``AutoField``           integer             serial              integer\n``BigAutoField``        integer             bigserial           bigint\n``IntegerField``        integer             integer             integer\n``BigIntegerField``     integer             bigint              bigint\n``SmallIntegerField``   integer             smallint            smallint\n``IdentityField``       not supported       int identity        not supported\n``FloatField``          real                real                real\n``DoubleField``         real                double precision    double precision\n``DecimalField``        decimal             numeric             numeric\n``CharField``           varchar             varchar             varchar\n``FixedCharField``      char                char                char\n``TextField``           text                text                text\n``BlobField``           blob                bytea               blob\n``BitField``            integer             bigint              bigint\n``BigBitField``         blob                bytea               blob\n``UUIDField``           text                uuid                varchar(40)\n``BinaryUUIDField``     blob                bytea               varbinary(16)\n``DateTimeField``       datetime            timestamp           datetime\n``DateField``           date                date                date\n``TimeField``           time                time                time\n``TimestampField``      integer             integer             integer\n``IPField``             integer             bigint              bigint\n``BooleanField``        integer             boolean             bool\n``BareField``           untyped             not supported       not supported\n``ForeignKeyField``     integer             integer             integer\n=====================   =================   =================   =================\n\n.. seealso::\n   * SQLite fields for JSON, Full-Text Search: :ref:`sqlite`\n   * Postgresql fields for Arrays, JSON, Full-Text Search, HStore: :ref:`postgresql`\n   * MySQL fields for JSON: :ref:`mysql`\n   * Extra fields (extension): :ref:`extra-fields`\n   * :ref:`custom-fields`\n\nCommon field parameters\n^^^^^^^^^^^^^^^^^^^^^^^\n\nAll field types accept the following keyword arguments:\n\n================ ========= =======================================================================\nParameter        Default   Description\n================ ========= =======================================================================\n``null``         ``False`` allow null values\n``index``        ``False`` create an index on this column\n``unique``       ``False`` create a unique index on this column.\n                           See also :ref:`adding composite indexes <model_indexes>`.\n``column_name``  ``None``  explicitly specify the column name in the database.\n``default``      ``None``  any value or callable to use as a default for uninitialized models\n``primary_key``  ``False`` primary key for the table\n``constraints``  ``None``  one or more constraints, e.g. ``[Check('price > 0')]``\n``sequence``     ``None``  sequence name (if backend supports it)\n``collation``    ``None``  collation to use for ordering the field / index\n``unindexed``    ``False`` indicate field on virtual table should be unindexed (**SQLite-only**)\n``choices``      ``None``  optional iterable containing 2-tuples of ``value``, ``display``\n``help_text``    ``None``  string representing any helpful text for this field\n``verbose_name`` ``None``  string representing the \"user-friendly\" name of this field\n``index_type``   ``None``  specify a custom index-type, e.g. for Postgres you might\n                           specify a ``'BRIN'`` or ``'GIN'`` index.\n================ ========= =======================================================================\n\nSpecial parameters\n^^^^^^^^^^^^^^^^^^\n\n+-----------------------------+------------------------------------------------+\n| Field type                  | Special Parameters                             |\n+=============================+================================================+\n| :class:`ForeignKeyField`    | ``model``, ``field``, ``backref``,             |\n|                             | ``on_delete``, ``on_update``, ``deferrable``   |\n|                             | ``lazy_load``                                  |\n+-----------------------------+------------------------------------------------+\n| :class:`CharField`          | ``max_length``                                 |\n+-----------------------------+------------------------------------------------+\n| :class:`FixedCharField`     | ``max_length``                                 |\n+-----------------------------+------------------------------------------------+\n| :class:`DateTimeField`      | ``formats``                                    |\n+-----------------------------+------------------------------------------------+\n| :class:`DateField`          | ``formats``                                    |\n+-----------------------------+------------------------------------------------+\n| :class:`TimeField`          | ``formats``                                    |\n+-----------------------------+------------------------------------------------+\n| :class:`TimestampField`     | ``resolution``, ``utc``                        |\n+-----------------------------+------------------------------------------------+\n| :class:`DecimalField`       | ``max_digits``, ``decimal_places``,            |\n|                             | ``auto_round``, ``rounding``                   |\n+-----------------------------+------------------------------------------------+\n| :class:`BareField`          | ``adapt``                                      |\n+-----------------------------+------------------------------------------------+\n\n.. note::\n   Both ``default`` and ``choices`` could be implemented at the database level\n   as *DEFAULT* and *CHECK CONSTRAINT* respectively, but any application\n   change would require a schema change. Because of this, ``default`` is\n   implemented purely in python and ``choices`` are not validated but exist\n   for metadata purposes only.\n\n   To add database (server-side) constraints, use the ``constraints``\n   parameter:\n\n   .. code-block:: python\n\n      class Product(Model):\n          price = DecimalField(max_digits=8, decimal_places=2,\n                               constraints=[Check('price >= 0')])\n          added = DateTimeField(constraints=[Default('CURRENT_TIMESTAMP')])\n          status = IntegerField(constraints=[Check('status in (0, 1, 2)')])\n\nDefault field values\n^^^^^^^^^^^^^^^^^^^^\n\nPeewee can provide default values for fields when objects are created. For\nexample to have an ``IntegerField`` default to zero rather than ``NULL``, you\ncould declare the field with a default value:\n\n.. code-block:: python\n\n   class Message(Model):\n       context = TextField()\n       read_count = IntegerField(default=0)\n       created = DateTimeField(default=datetime.datetime.now)\n\nFor ``read_count``, Peewee uses the literal value ``0``. For ``created``,\nPeewee calls ``datetime.datetime.now`` at the moment of instantiation -\nnote that the **function itself is passed, not its return value**.\n\n**Mutable defaults require a factory function.** If a default value is a mutable\nobject such as a ``list`` or ``dict``, passing it directly means every model\ninstance shares *the same object*. Wrap it in a function instead:\n\n.. code-block:: python\n\n   # Wrong: all instances share one dict.\n   class Config(BaseModel):\n       settings = JSONField(default={})\n\n   # Correct: each instance gets a fresh dict.\n   def default_settings():\n       return {}\n\n   class Config(BaseModel):\n       settings = JSONField(default=default_settings)\n\nThe database can also provide the default value for a field. While Peewee does\nnot explicitly provide an API for setting a server-side default value, you can\nuse the ``constraints`` and :func:`Default` to specify the server default:\n\n.. code-block:: python\n\n   class Message(Model):\n       content = TextField()\n       timestamp = DateTimeField(constraints=[Default('CURRENT_TIMESTAMP')])\n\nThis produces a ``DEFAULT CURRENT_TIMESTAMP`` clause in the ``CREATE TABLE``\nstatement. Peewee's own ``default`` parameter produces no DDL; it only operates\nduring Python-side model instantiation.\n\nA consequence of using server-generated defaults is that newly-inserted models\nwill not automatically retrieve the new value. This requires a separate query\nto read back the defaults added by the server.\n\nForeignKeyField\n---------------\n\n:class:`ForeignKeyField` links a model to another model. It stores the\nrelated row's primary key as an integer column and provides a Python-level\ndescriptor that resolves it to a full model instance on access.\n\n.. code-block:: python\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n\nThe ``backref`` parameter creates a reverse accessor on the target model.\nWith ``backref='tweets'``, every ``User`` instance gains a ``tweets``\nattribute that returns a pre-filtered :class:`Select` query of that user's\ntweets.\n\n:class:`ForeignKeyField` accepts referential action parameters:\n\n- ``on_delete`` - action to take when the referenced row is deleted.\n  Common values: ``'CASCADE'``, ``'SET NULL'``, ``'RESTRICT'``.\n- ``on_update`` - action to take when the referenced row's primary key changes.\n- ``deferrable`` - defers constraint checking to transaction commit\n  (Postgresql and SQLite only).\n\n.. warning::\n   SQLite does not enforce foreign key constraints by default. Enable\n   enforcement by setting the ``foreign_keys`` pragma on connection:\n\n   .. code-block:: python\n\n      db = SqliteDatabase('my_app.db', pragmas={'foreign_keys': 1})\n\n.. seealso::\n   :ref:`relationships` covers how foreign keys behave at runtime, including\n   lazy loading, back-references, and avoiding N+1 query problems.\n\nTypically a foreign key will reference the primary key of the related model,\nbut you can specify a particular column by specifying ``field=``.\n\nIn Peewee, accessing the value of a :class:`ForeignKeyField` will return the\nentire related object:\n\n.. code-block:: python\n\n   tweets = (Tweet\n             .select(Tweet, User)\n             .join(User)\n             .order_by(Tweet.created_date.desc()))\n\n   for tweet in tweets:\n       print(tweet.user.username, tweet.message)\n\nIn the example above the ``User`` data was selected efficiently. If we did not\nselect the ``User``, then an **additional query** would be needed to fetch the\nassociated ``User`` data:\n\n.. code-block:: python\n\n    tweets = (Tweet\n              .select()\n              .order_by(Tweet.created_date.desc())\n\n    for tweet in tweets:\n        # WARNING: an additional query will be issued for EACH tweet\n        # to fetch the associated User data.\n        print(tweet.user.username, tweet.message)\n\nSometimes you only need the associated primary key value from the foreign key\ncolumn. Peewee allows you to access the raw foreign key value by appending\n``\"_id\"`` to the foreign key field's name:\n\n.. code-block:: python\n\n   tweets = Tweet.select()\n\n   for tweet in tweets:\n       # Instead of \"tweet.user\", we will just get the raw ID value stored\n       # in the column.\n       print(tweet.user_id, tweet.message)\n\nTo prevent accidentally resolving a foreign-key and triggering an additional\nquery, :class:`ForeignKeyField` supports an initialization paramater\n``lazy_load`` which, when disabled, behaves like the ``\"_id\"`` attribute:\n\n.. code-block:: python\n\n   class Tweet(Model):\n       # lazy-load disabled:\n       user = ForeignKeyField(User, backref='tweets', lazy_load=False)\n       ...\n\n   for tweet in Tweet.select():\n       print(tweet.user, tweet.message)\n\n   # With lazy-load disabled, accessing tweet.user will NOT perform an extra\n   # query and the user ID value is returned instead.\n   # e.g.:\n   # 1  tweet from user1\n   # 1  another from user1\n   # 2  tweet from user2\n\n   # However, if we eagerly load the related user object, then the user\n   # foreign key will behave like usual:\n   for tweet in Tweet.select(Tweet, User).join(User):\n       print(tweet.user.username, tweet.message)\n\n   # user1  tweet from user1\n   # user1  another from user1\n   # user2  tweet from user1\n\nForeignKeyField Back-references\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:class:`ForeignKeyField` allows for a back-reference property to be bound to\nthe target model. This property will be named ``<classname>_set`` by default,\nwhere ``classname`` is the lowercase name of the model class. This name can be\noverridden by specifying ``backref=``:\n\n.. code-block:: python\n\n   class Message(Model):\n       from_user = ForeignKeyField(User, backref='outbox')\n       to_user = ForeignKeyField(User, backref='inbox')\n       text = TextField()\n\n   for message in some_user.outbox:\n       # We are iterating over all Messages whose from_user is some_user.\n       print(message)\n\nBack-references are just pre-filtered select queries, so we can add\nadditional behavior like ``order_by()``:\n\n.. code-block:: python\n\n   for message in some_user.inbox.order_by(Message.id):\n       # Iterate over all Messages whose to_user is some_user.\n       print(message)\n\nSelf-referential foreign keys\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen creating a hierarchical structure it is necessary to create a\nself-referential foreign key which links a child object to its parent. Because\nthe model class is not defined at the time you instantiate the self-referential\nforeign key, use the special string ``'self'`` to indicate a self-referential\nforeign key:\n\n.. code-block:: python\n\n   class Category(Model):\n       name = CharField()\n       parent = ForeignKeyField('self', null=True, backref='children')\n\nThe foreign key points **upward** to the parent object and the back-reference\nis named **children**.\n\n.. attention:: Self-referential foreign-keys should always be ``null=True``.\n\nWhen querying against a model that contains a self-referential foreign key you\nmay sometimes need to perform a self-join. In those cases you can use\n:meth:`Model.alias` to create a table reference. Here is how you might query\nthe category and parent model using a self-join:\n\n.. code-block:: python\n\n   Parent = Category.alias()\n   GrandParent = Category.alias()\n   query = (Category\n            .select(Category, Parent)\n            .join(Parent, on=(Category.parent == Parent.id))\n            .join(GrandParent, on=(Parent.parent == GrandParent.id))\n            .where(GrandParent.name == 'some category')\n            .order_by(Category.name))\n\nFor deeply nested hierarchies, recursive CTEs are more efficient than\nrepeated self-joins. See :ref:`cte`.\n\n.. seealso:: :ref:`relationships`\n\nDate and Time Fields\n--------------------\n\nThe three fields devoted to working with dates and times have properties to\naccess date attributes like year, month, hour, etc.\n\n:class:`DateField`\n   Properties for: ``year``, ``month``, ``day``\n\n:class:`TimeField`\n   Properties for: ``hour``, ``minute``, ``second``\n\n:class:`DateTimeField`:\n   Properties for: ``year``, ``month``, ``day``, ``hour``, ``minute``, ``second``\n\nThese properties can be used as an expression in a query. Let's say we have\nan events table and want to list all the days in the current month which have\nat least one event:\n\n.. code-block:: python\n\n    # Get the current date.\n    today = datetime.date.today()\n\n    # Get days that have events for the current month.\n    query = (Event\n             .select(Event.event_date.day.alias('day'))\n             .where(\n                 (Event.event_date.year == today.year) &\n                 (Event.event_date.month == today.month))\n             .distinct())\n\n   # Group activity by hour of day.\n   query = (PageView\n            .select(\n                PageView.timestamp.hour.alias('hour'),\n                fn.COUNT(PageView.id).alias('n'))\n            .group_by(PageView.timestamp.hour)\n            .order_by(PageView.timestamp.hour))\n\n.. note::\n   SQLite does not have a native date type, so dates are stored in formatted\n   text columns. To ensure that comparisons work correctly, the dates need to\n   be formatted so they are sorted lexicographically. That is why they are\n   stored, by default, as ``YYYY-MM-DD HH:MM:SS``.\n\n:class:`TimestampField` stores a datetime as a Unix timestamp integer.\nThe ``resolution`` parameter controls sub-second precision (default: seconds);\n``utc=True`` instructs Peewee to treat stored values as UTC.\n\nBitField and BigBitField\n------------------------\n\nThe :class:`BitField` and :class:`BigBitField` are suitable for storing\nbitmap data. :class:`BitField` provides a subclass of :class:`IntegerField`\nthat is suitable for storing feature toggles as an integer bitmask. The latter\nis suitable for storing a bitmap for a large data-set, e.g. expressing\nmembership or bitmap-type data.\n\nAs an example of using :class:`BitField`, let's say we have a *Post* model\nand we wish to store certain True/False flags about how the post. We could\nstore all these feature toggles in their own :class:`BooleanField` objects,\nor we could use a single :class:`BitField` instead:\n\n.. code-block:: python\n\n   class Post(Model):\n       content = TextField()\n       flags = BitField()\n\n       is_favorite = flags.flag(1)\n       is_sticky = flags.flag(2)\n       is_minimized = flags.flag(4)\n       is_deleted = flags.flag(8)\n\nUsing these flags is quite simple:\n\n.. code-block:: pycon\n\n   >>> p = Post()\n   >>> p.is_sticky = True\n   >>> p.is_minimized = True\n   >>> print(p.flags)  # Prints 4 | 2 --> \"6\"\n   6\n   >>> p.is_favorite\n   False\n   >>> p.is_sticky\n   True\n\nWe can also use the flags on the Post class to build expressions in queries:\n\n.. code-block:: python\n\n   # Generates a WHERE clause that looks like:\n   # WHERE (post.flags & 1 != 0)\n   favorites = Post.select().where(Post.is_favorite)\n\n   # Query for sticky + favorite posts:\n   sticky_faves = Post.select().where(Post.is_sticky & Post.is_favorite)\n\nSince the :class:`BitField` is stored in an integer, there is a maximum of\n64 flags you can represent (64-bits is common size of integer column). For\nstoring arbitrarily large bitmaps, you can instead use :class:`BigBitField`,\nwhich uses an automatically managed buffer of bytes, stored in a\n:class:`BlobField`.\n\nWhen bulk-updating one or more bits in a :class:`BitField`, you can use\nbitwise operators to set or clear one or more bits:\n\n.. code-block:: python\n\n   # Set the 4th bit on all Post objects.\n   Post.update(flags=Post.flags | 8).execute()\n\n   # Clear the 1st and 3rd bits on all Post objects.\n   Post.update(flags=Post.flags & ~(1 | 4)).execute()\n\nFor simple operations, the flags provide handy ``set()`` and ``clear()``\nmethods for setting or clearing an individual bit:\n\n.. code-block:: python\n\n   # Set the \"is_deleted\" bit on all posts.\n   Post.update(flags=Post.is_deleted.set()).execute()\n\n   # Clear the \"is_deleted\" bit on all posts.\n   Post.update(flags=Post.is_deleted.clear()).execute()\n\nExample usage:\n\n.. code-block:: python\n\n   class Bitmap(Model):\n       data = BigBitField()\n\n   bitmap = Bitmap()\n\n   # Sets the ith bit, e.g. the 1st bit, the 11th bit, the 63rd, etc.\n   bits_to_set = (1, 11, 63, 31, 55, 48, 100, 99)\n   for bit_idx in bits_to_set:\n       bitmap.data.set_bit(bit_idx)\n\n   # We can test whether a bit is set using \"is_set\":\n   assert bitmap.data.is_set(11)\n   assert not bitmap.data.is_set(12)\n\n   # We can clear a bit:\n   bitmap.data.clear_bit(11)\n   assert not bitmap.data.is_set(11)\n\n   # We can also \"toggle\" a bit. Recall that the 63rd bit was set earlier.\n   assert bitmap.data.toggle_bit(63) is False\n   assert bitmap.data.toggle_bit(63) is True\n   assert bitmap.data.is_set(63)\n\n   # BigBitField supports item accessor by bit-number, e.g.:\n   assert bitmap.data[63]\n   bitmap.data[0] = 1\n   del bitmap.data[0]\n\n   # We can also combine bitmaps using bitwise operators, e.g.\n   b = Bitmap(data=b'\\x01')\n   b.data |= b'\\x02'\n   assert list(b.data) == [1, 1, 0, 0, 0, 0, 0, 0]\n   assert len(b.data) == 1\n\n.. _model-options:\n\nModel Settings\n--------------\n\nModel-specific configuration is placed in a special :class:`Metadata` class\ncalled ``Meta``:\n\n.. code-block:: python\n   :emphasize-lines: 8, 9\n\n   from peewee import *\n\n   contacts_db = SqliteDatabase('contacts.db')\n\n   class Person(Model):\n       name = CharField()\n\n       class Meta:\n           database = contacts_db\n\nThis instructs Peewee that whenever a query is executed on *Person* to use the\ncontacts database.\n\nOnce the class is defined metadata settings are accessible at ``ModelClass._meta``:\n\n.. code-block:: pycon\n\n   >>> Person.Meta\n   Traceback (most recent call last):\n     File \"<stdin>\", line 1, in <module>\n   AttributeError: type object 'Person' has no attribute 'Meta'\n\n   >>> Person._meta\n   <peewee.Metadata object at 0x7f51a2f03790>\n\nThe :class:`Metadata` class implements several methods which may be of use for\nretrieving model metadata (such as lists of fields, foreign key relationships,\nand more).\n\n.. code-block:: pycon\n\n   >>> User._meta.fields\n   {'id': <peewee.AutoField object at 0x7f51a2e92750>,\n    'username': <peewee.CharField object at 0x7f51a2f0a510>}\n\n   >>> User._meta.primary_key\n   <peewee.AutoField object at 0x7f51a2e92750>\n\n   >>> User._meta.database\n   <peewee.SqliteDatabase object at 0x7f519bff6dd0>\n\nThere are several options you can specify as ``Meta`` attributes. While most\noptions are inheritable, some are table-specific and will not be inherited by\nsubclasses.\n\n=========================  ======================================================  ============\nOption                     Purpose                                                 Inheritable\n=========================  ======================================================  ============\n``database``               Database instance for this model.                       Yes\n``table_name``             Explicit table name.                                    No\n``table_function``         Callable that returns a table name from the class.      Yes\n``indexes``                Tuple of multi-column index definitions.                Yes\n``primary_key``            :class:`CompositeKey` or ``False``.                     Yes\n``constraints``            List of table-level constraint expressions.             Yes\n``schema``                 Database schema name.                                   Yes\n``only_save_dirty``        Only emit changed fields on ``save()``.                 Yes\n``options``                Extra options for ``CREATE TABLE`` extensions.          Yes\n``table_settings``         Strings appended after the closing parenthesis in DDL.  Yes\n``temporary``              Mark as a temporary table.                              Yes\n``legacy_table_names``     Use legacy (non-snake-case) table name generation.      Yes\n``depends_on``             Declare a dependency on another table for ordering.     No\n``without_rowid``          SQLite ``WITHOUT ROWID`` tables.                        No\n``strict_tables``          SQLite strict typing (3.37+).                           Yes\n=========================  ======================================================  ============\n\nExample of inheritable vs non-inheritable settings:\n\n.. code-block:: pycon\n\n   >>> db = SqliteDatabase(':memory:')\n   >>> class ModelOne(Model):\n   ...     class Meta:\n   ...         database = db\n   ...         table_name = 'model_one_tbl'\n   ...\n   >>> class ModelTwo(ModelOne):\n   ...     pass\n   ...\n   >>> ModelOne._meta.database is ModelTwo._meta.database\n   True\n   >>> ModelOne._meta.table_name == ModelTwo._meta.table_name\n   False\n\n.. _table-names:\n\nTable naming\n^^^^^^^^^^^^\n\nBy default Peewee derives the table name from the model class name. The exact\ntransformation depends on ``Meta.legacy_table_names``:\n\n=================== =========================  ==============================\nModel class name    legacy (default)           non-legacy\n=================== =========================  ==============================\n``User``            ``user``                   ``user``\n``UserProfile``     ``userprofile``            ``user_profile``\n``APIResponse``     ``apiresponse``            ``api_response``\n``WebHTTPRequest``  ``webhttprequest``          ``web_http_request``\n=================== =========================  ==============================\n\nNew projects should opt into non-legacy naming by setting\n``legacy_table_names = False`` on ``BaseModel``. The legacy default exists\nonly for backwards compatibility with existing deployments.\n\n.. code-block:: python\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n           legacy_table_names = False   # Recommended for new projects.\n\nTo override the table name entirely, use ``table_name``:\n\n.. code-block:: python\n\n   class UserProfile(BaseModel):\n       class Meta:\n           table_name = 'acct_user_profile'   # Maps to pre-existing table.\n\nTo apply a naming convention programmatically across all models, use\n``table_function``:\n\n.. code-block:: python\n\n   def prefixed_table_name(model_class):\n       return 'myapp_' + model_class.__name__.lower()\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n           table_function = prefixed_table_name\n\n   class User(BaseModel):\n       pass   # Table name: \"myapp_user\"\n\n.. _model_indexes:\n\nIndexes and Constraints\n-----------------------\n\nPeewee can create indexes on single or multiple columns, optionally including a\n*UNIQUE* constraint. Peewee also supports user-defined constraints on both\nmodels and fields.\n\nSingle-column indexes and constraints\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSingle column indexes are defined by specifying ``index=True`` or\n``unique=True`` when declaring the Field.\n\nAdd a unique index on *username* and a normal b-tree index on *email*:\n\n.. code-block:: python\n\n   class User(Model):\n       username = CharField(unique=True)\n       email = CharField(index=True)\n\nTo add a user-defined constraint on a column, you can specify it using the\n``constraints`` parameter. You may wish to specify a default value as part of\nthe schema, or add a ``CHECK`` constraint, for example:\n\n.. code-block:: python\n\n   class Product(Model):\n       name = CharField(unique=True)\n       price = DecimalField(constraints=[Check('price < 10000')])\n       created = DateTimeField(constraints=[Default('CURRENT_TIMESTAMP')])\n\nMulti-column indexes\n^^^^^^^^^^^^^^^^^^^^\n\nMulti-column indexes may be defined as *Meta* attributes using a nested tuple.\nEach database index is a 2-tuple, the first part of which is a tuple of the\nnames of the fields, the second part a boolean indicating whether the index\nshould be unique.\n\n.. code-block:: python\n\n   class Transaction(Model):\n       from_acct = CharField()\n       to_acct = CharField()\n       amount = DecimalField()\n       date = DateTimeField()\n\n       class Meta:\n           indexes = (\n               # create a unique on from/to/date\n               (('from_acct', 'to_acct', 'date'), True),\n\n               # create a non-unique on from/to\n               (('from_acct', 'to_acct'), False),\n           )\n\n.. note::\n   Remember to add a **trailing comma** if your tuple of indexes contains only one item:\n\n   .. code-block:: python\n\n      class Meta:\n          indexes = (\n              (('first_name', 'last_name'), True),  # Note the trailing comma!\n          )\n\nPartial and expression indexes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nPartial indexes, indexes with expressions, and more complex indexes can use the\n:meth:`Model.add_index` API:\n\n.. code-block:: python\n\n   class Article(BaseModel):\n       name = TextField()\n       timestamp = TimestampField()\n       status = IntegerField()\n\n   # Add a partial index on name and timestamp where status = 1.\n   Article.add_index(Article.name, Article.timestamp,\n                     where=(Article.status == 1))\n\n   # Create a unique index on timestamp desc, status & 4.\n   idx = Article.index(\n       Article.timestamp.desc(),\n       Article.flags.bin_and(4),\n       unique=True)\n   Article.add_index(idx)\n\n.. note::\n   SQLite does not support parameterized ``CREATE INDEX`` queries. Partial\n   indexes and expression indexes on SQLite must be written using\n   :class:`SQL`:\n\n   .. code-block:: python\n\n      Article.add_index(SQL('CREATE INDEX ... WHERE status = 1'))\n\nIf the above is cumbersome, you can also pass a :class:`SQL` instance to\n``Meta.indexes``:\n\n.. code-block:: python\n\n   class Article(BaseModel):\n       name = TextField()\n       timestamp = TimestampField()\n       status = IntegerField()\n\n       class Meta:\n           indexes = [\n               SQL('CREATE INDEX article_published_lookup ON '\n                   'article (name, timestamp) WHERE status = 1'),\n           ]\n\nPrimary Keys\n------------\n\nAuto-incrementing integer primary key\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIf a model declares no primary key, Peewee automatically adds an\nauto-incrementing integer field named ``id``:\n\n.. code-block:: python\n\n   class Article(BaseModel):\n       title = TextField()\n       # Peewee implicitly adds: id = AutoField()\n\nTo use a different name for the auto-incrementing primary key, declare an\n:class:`AutoField` explicitly:\n\n.. code-block:: python\n\n   class Article(BaseModel):\n       article_id = AutoField()\n       title = TextField()\n\n.. warning::\n   A common mistake is writing ``id = IntegerField(primary_key=True)`` when\n   intending an auto-incrementing primary key. This declares a plain integer\n   column whose value the application must supply - the database will not\n   generate it. Use :class:`AutoField` for auto-increment behavior.\n\nNon-integer primary keys\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nAny field can serve as the primary key by passing ``primary_key=True``:\n\n.. code-block:: python\n\n   class Country(BaseModel):\n       code = CharField(max_length=2, primary_key=True)   # e.g. 'US', 'DE'\n       name = TextField()\n\nWhen using a non-auto-incrementing primary key, Peewee cannot distinguish\nbetween a new row (needs ``INSERT``) and an existing row (needs ``UPDATE``)\nby checking whether the primary key is ``None``. On the first save, pass\n``force_insert=True`` explicitly:\n\n.. code-block:: python\n\n   country = Country(code='DE', name='Germany')\n   country.save(force_insert=True)   # First save: must force INSERT.\n   country.name = 'Deutschland'\n   country.save()                    # Subsequent saves: UPDATE as normal.\n\n:meth:`Model.create` handles this automatically, so it is the simpler\noption for one-step creation:\n\n.. code-block:: python\n\n   country = Country.create(code='DE', name='Germany')\n\n.. _composite-keys:\n\nComposite primary keys\n^^^^^^^^^^^^^^^^^^^^^^\n\nUse :class:`CompositeKey` in ``Meta.primary_key`` to designate two or more\ncolumns as a composite primary key:\n\n.. code-block:: python\n\n   class TweetTag(BaseModel):\n       tweet = ForeignKeyField(Tweet)\n       tag = TextField()\n\n       class Meta:\n           primary_key = CompositeKey('tweet', 'tag')\n\nComposite primary keys are most appropriate for junction tables in many-to-many\nrelationships. Peewee has limited support for foreign keys *to* models with\ncomposite primary keys; avoid them in models that other models will reference.\n\nModels without a primary key\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo create a table with no primary key, set ``primary_key = False``:\n\n.. code-block:: python\n\n   class LogEntry(BaseModel):\n       timestamp = DateTimeField()\n       event = TextField()\n\n       class Meta:\n           primary_key = False\n\nNote that :meth:`Model.save` and :meth:`Model.delete_instance` do not\nwork on keyless models, since both require a primary key to target a specific\nrow. Use :meth:`Model.insert`, :meth:`Model.update`, and :meth:`Model.delete`\n(the class-level query methods) instead.\n\nTable Constraints\n-----------------\n\nPeewee allows arbitrary constraints to :class:`Model` classes.\n\nSuppose you have a *people* table with a composite primary key of two columns:\nthe person's first and last name. You wish to have another table relate to the\n*people* table. To do this define a multi-column foreign key constraint:\n\n.. code-block:: python\n\n   class Person(Model):\n       first = CharField()\n       last = CharField()\n\n       class Meta:\n           primary_key = CompositeKey('first', 'last')\n\n   class Pet(Model):\n       owner_first = CharField()\n       owner_last = CharField()\n       pet_name = CharField()\n\n       class Meta:\n           constraints = [SQL('FOREIGN KEY(owner_first, owner_last) '\n                              'REFERENCES person(first, last)')]\n\n``CHECK`` constraints can be specified at the table level:\n\n.. code-block:: python\n\n   class Product(Model):\n       name = CharField(unique=True)\n       price = DecimalField()\n\n       class Meta:\n           constraints = [Check('price < 10000')]\n\nCreating Tables\n---------------\n\nOnce models are defined, create their corresponding tables with\n:meth:`Database.create_tables`:\n\n.. code-block:: python\n\n   db.create_tables([User, Tweet, Favorite])\n\nTo create a single table, use :meth:`Model.create_table`:\n\n.. code-block:: python\n\n   Tweet.create_table()\n\n.. seealso::\n   :ref:`schema` for documentation on table creation and other schema\n   management tasks.\n\n.. _advanced-model-topics:\n\nAdvanced Topics\n---------------\n\nThe following sections cover scenarios that arise less frequently. New users\ncan skip this section and return to it when the need arises.\n\n.. _circular-fks:\n\nCircular foreign key dependencies\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSometimes it happens that you will create a circular dependency between two\ntables.\n\n.. note::\n   Circular foreign keys should be refactored (by adding an intermediary table,\n   for instance).\n\nAdding circular foreign keys with peewee is a bit tricky because at the time\nyou are defining either foreign key, the model it points to will not have been\ndefined yet, causing a ``NameError``.\n\n.. code-block:: python\n   :emphasize-lines: 3\n\n   class User(Model):\n       username = CharField()\n       favorite_tweet = ForeignKeyField(Tweet, null=True)  # NameError!!\n\n   class Tweet(Model):\n       message = TextField()\n       user = ForeignKeyField(User, backref='tweets')\n\nOne option is to simply use an :class:`IntegerField` to store the raw ID:\n\n.. code-block:: python\n\n   class User(Model):\n       username = CharField()\n       favorite_tweet_id = IntegerField(null=True)\n\nBy using :class:`DeferredForeignKey` we can get around the problem and still\nuse a foreign key field:\n\n.. code-block:: python\n\n   class User(BaseModel):\n       username = TextField()\n       favorite_tweet = DeferredForeignKey('Tweet', null=True)\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n\n   db.create_tables([User, Tweet])\n\n   # Add the constraint that could not be created at table-creation time.\n   User._schema.create_foreign_key(User.favorite_tweet)\n\n.. note::\n   Because SQLite has limited support for altering tables, foreign-key\n   constraints cannot be added to a table after it has been created.\n\nField naming conflicts\n^^^^^^^^^^^^^^^^^^^^^^\n\nSeveral names are reserved by :class:`Model` for built-in methods and\nattributes (for example ``save``, ``create``, ``delete``, ``update``,\n``get``). Declaring a field with one of these names overwrites the method.\n\nWhen the desired column name conflicts with a model method, supply an\nalternative attribute name and set ``column_name`` explicitly:\n\n.. code-block:: python\n\n   class LogEntry(BaseModel):\n       timestamp = DateTimeField()\n       # \"create\" and \"update\" would conflict with Model.create / Model.update.\n       created_at = DateTimeField(column_name='create')\n       updated_at = DateTimeField(column_name='update')\n\nThe database column is still named ``create`` and ``update``; the Python\nattributes are ``created_at`` and ``updated_at``.\n\n.. _barefield:\n\nBareField (SQLite only)\n^^^^^^^^^^^^^^^^^^^^^^^\n\n:class:`BareField` declares a column with no type affinity. It is only\nmeaningful with SQLite, which permits untyped columns and virtual table\ncolumns.\n\n.. code-block:: python\n\n   class FTSEntry(BaseModel):\n       content = BareField()\n\nThe optional ``adapt`` parameter specifies a callable that converts values\ncoming from the database into a Python type:\n\n.. code-block:: python\n\n   class RawData(BaseModel):\n       value = BareField(adapt=float)\n\nFor full-text search virtual tables, use :class:`SearchField` rather\nthan :class:`BareField`. See :ref:`sqlite-fts`.\n\n.. _custom-fields:\n\nCustom fields\n^^^^^^^^^^^^^\n\nA custom field is a subclass of an existing field that overrides the\nPython-to-database and database-to-Python conversion methods. This is most\nuseful when a database offers a column type that has no built-in Peewee\nequivalent, or when a standard column type should carry application-specific\nPython behavior.\n\nThe two conversion hooks are:\n\n- ``db_value(self, value)`` - converts a Python value to the format the\n  database driver expects.\n- ``python_value(self, value)`` - converts a value from the database driver\n  into the desired Python type.\n\nThe following example implements a field that stores a ``pathlib.Path``\nvalue as a ``TEXT`` column:\n\n.. code-block:: python\n\n   from pathlib import Path\n\n   class PathField(TextField):\n       def db_value(self, value):\n           return str(value) if value is not None else None\n\n       def python_value(self, value):\n           return Path(value) if value is not None else None\n\n   class Document(BaseModel):\n       path = PathField()\n\n   doc = Document.create(path=Path('/var/data/report.pdf'))\n   assert isinstance(doc.path, Path)\n\nWhen the database requires a completely new storage type (not a variant of an\nexisting one), set ``field_type`` to the type label and register the label\nwith each database that will use it:\n\n.. code-block:: python\n\n   class PointField(Field):\n       field_type = 'point'   # Custom type label.\n\n       def db_value(self, value):\n           if value is not None:\n               return f'{value[0]},{value[1]}'\n\n       def python_value(self, value):\n           if value is not None:\n               x, y = value.split(',')\n               return (float(x), float(y))\n\n   # Tell Peewee what DDL type to emit for each database.\n   sq_db  = SqliteDatabase('mydb', field_types={'point': 'text'})\n\n.. seealso:: :class:`Field` API reference.\n"
  },
  {
    "path": "docs/peewee/mysql.rst",
    "content": ".. _mysql:\n\nMySQL and MariaDB\n=================\n\n.. module:: playhouse.mysql_ext\n\nPeewee provides alternate drivers for MySQL through ``playhouse.mysql_ext``.\n\n.. class:: MySQLConnectorDatabase(database, **kwargs)\n\n   Database implementation using the official `mysql-connector-python <https://dev.mysql.com/doc/connector-python/en/>`_\n   driver instead of ``pymysql``.\n\n   .. code-block:: python\n\n      from playhouse.mysql_ext import MySQLConnectorDatabase\n\n      db = MySQLConnectorDatabase('my_db', host='1.2.3.4', user='mysql')\n\n\n.. class:: PooledMySQLConnectorDatabase(database, **kwargs)\n\n   Connection-pooling variant of :class:`MySQLConnectorDatabase`.\n\n\n.. class:: MariaDBConnectorDatabase(database, **kwargs)\n\n   Database implementation using the `mariadb-connector <https://mariadb-corporation.github.io/mariadb-connector-python/>`_\n   driver.\n\n   .. note::\n      Does **not** accept ``charset``, ``sql_mode``, or ``use_unicode``\n      parameters (charset is always ``utf8mb4``).\n\n   .. code-block:: python\n\n      from playhouse.mysql_ext import MariaDBConnectorDatabase\n\n      db = MariaDBConnectorDatabase('my_db', host='1.2.3.4', user='mysql')\n\n.. class:: PooledMariaDBConnectorDatabase(database, **kwargs)\n\n   Connection-pooling variant of :class:`MariaDBConnectorDatabase`.\n\n\nMySQL-specific helpers:\n\n.. module:: playhouse.mysql_ext:\n\n.. class:: JSONField()\n\n   Extends :class:`TextField` with transparent JSON encoding/decoding.\n\n   .. method:: extract(path)\n\n      Extract a value from a JSON document at the given JSON path\n      (e.g. ``'$.key'``).\n\n.. function:: Match(columns, expr, modifier=None)\n\n   Helper for MySQL full-text search using ``MATCH ... AGAINST`` syntax.\n\n   :param columns: A single :class:`Field` or a tuple of fields.\n   :param str expr: Full-text search expression.\n   :param str modifier: Optional modifier, e.g. ``'IN BOOLEAN MODE'``.\n\n   .. code-block:: python\n\n       from playhouse.mysql_ext import Match\n\n       Post.select().where(\n           Match((Post.title, Post.body), 'python asyncio',\n                 modifier='IN BOOLEAN MODE'))\n"
  },
  {
    "path": "docs/peewee/orm_utils.rst",
    "content": ".. _orm-utils:\n\nORM Utilities\n=============\n\nThese modules provide higher-level abstractions on top of Peewee's core ORM\nand work with any database backend.\n\n.. contents:: On this page\n   :local:\n   :depth: 1\n\n\n.. _shortcuts:\n\nShortcuts\n---------\n\n.. module:: playhouse.shortcuts\n\n``playhouse.shortcuts`` provides helpers for serializing model instances to\nand from dictionaries, resolving compound queries, and thread-safe database\nswapping.\n\nModel Serialization\n^^^^^^^^^^^^^^^^^^^\n\n.. function:: model_to_dict(model, recurse=True, backrefs=False, only=None, exclude=None, extra_attrs=None, fields_from_query=None, max_depth=None, manytomany=False)\n\n   Convert a model instance to a dictionary.\n\n   :param bool recurse: Follow foreign keys and include the related object\n       as a nested dict (default: ``True``).\n   :param bool backrefs: Follow back-references and include related\n       collections as nested lists of dicts.\n   :param only: A list or set of field instances to include exclusively.\n   :param exclude: A list or set of field instances to exclude.\n   :param extra_attrs: A list of attribute or method names to include in\n       the output dict.\n   :param Select fields_from_query: Restrict serialization to only the\n       fields that were explicitly selected in the generating query.\n   :param int max_depth: Maximum depth when following relations.\n   :param bool manytomany: Include many-to-many fields.\n\n   Examples:\n\n   .. code-block:: python\n\n       user = User.create(username='alice')\n       model_to_dict(user)\n       # {'id': 1, 'username': 'alice'}\n\n       model_to_dict(user, backrefs=True)\n       # {'id': 1, 'username': 'alice', 'tweets': []}\n\n       t = Tweet.create(user=user, content='hello')\n       model_to_dict(t)\n       # {'id': 1, 'content': 'hello', 'user': {'id': 1, 'username': 'alice'}}\n\n       model_to_dict(t, recurse=False)\n       # {'id': 1, 'content': 'hello', 'user': 1}\n\n       model_to_dict(user, backrefs=True)\n       # {'id': 1, 'tweets': [{'id': 1, 'content': 'hello'}], 'username': 'alice'}\n\n   .. note::\n       If your use case is unusual, write a small custom function rather\n       than trying to coerce ``model_to_dict`` with a complex combination\n       of parameters.\n\n.. function:: dict_to_model(model_class, data, ignore_unknown=False)\n\n   Construct a model instance from a dictionary. Foreign keys may be\n   provided as nested dicts; back-references as lists of dicts.\n\n   :param Model model_class: The model class to construct.\n   :param dict data: A dictionary of data. Foreign keys can be included as nested dictionaries, and back-references as lists of dictionaries.\n   :param bool ignore_unknown: Allow keys that do not correspond to any\n       field on the model.\n\n   .. code-block:: python\n\n       user = dict_to_model(User, {'id': 1, 'username': 'alice'})\n       user.username   # 'alice'\n\n       # Nested foreign key:\n       tweet = dict_to_model(Tweet, {\n           'id': 1, 'content': 'hi',\n           'user': {'id': 1, 'username': 'alice'}})\n       tweet.user.username   # 'alice'\n\n.. function:: update_model_from_dict(instance, data, ignore_unknown=False)\n\n   Update an existing model instance with values from a dictionary.\n   Follows the same rules as :func:`dict_to_model`.\n\n   :param Model instance: The model instance to update.\n   :param dict data: A dictionary of data. Foreign keys can be included as nested dictionaries, and back-references as lists of dictionaries.\n   :param bool ignore_unknown: Allow keys that do not correspond to any\n       field on the model.\n\n\nCompound Query Resolution\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. function:: resolve_multimodel_query(query, key='_model_identifier')\n\n   Resolve rows from a compound ``UNION`` or similar query to the correct\n   model class. Useful when two tables are unioned and you need each row\n   as an instance of the appropriate model.\n\n   :param query: A compound :class:`SelectQuery`.\n   :param str key: Name of the column used to identify the model.\n   :returns: An iterable that yields properly typed model instances.\n\n\nThread-Safe Database Swapping\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. class:: ThreadSafeDatabaseMetadata()\n\n   Model :class:`Metadata` implementation that enables the ``database``\n   attribute to safely changed in a multi-threaded application. Use this when\n   your application may swap the active database (e.g. primary / read replica)\n   at runtime across threads:\n\n   .. code-block:: python\n\n       from playhouse.shortcuts import ThreadSafeDatabaseMetadata\n\n       primary  = PostgresqlDatabase('main')\n       replica  = PostgresqlDatabase('replica')\n\n       class BaseModel(Model):\n           class Meta:\n               database = primary\n               model_metadata_class = ThreadSafeDatabaseMetadata\n\n       # Safe to do at runtime from any thread:\n       BaseModel._meta.database = replica\n\n.. _pydantic:\n\nPydantic Integration\n--------------------\n\n.. module:: playhouse.pydantic_utils\n\n``playhouse.pydantic_utils`` generates `Pydantic v2 <https://docs.pydantic.dev/latest/>`_\nmodels from Peewee :class:`Model` classes using the :func:`~playhouse.pydantic_utils.to_pydantic`\nfunction.\n\nExample\n^^^^^^^\n\n.. code-block:: python\n\n   import datetime\n   from peewee import *\n   from playhouse.pydantic_utils import to_pydantic\n\n   db = SqliteDatabase(':memory:')\n\n   class User(db.Model):\n       name = CharField(verbose_name='Full Name', help_text='Display name')\n       age = IntegerField()\n       active = BooleanField(default=True)\n       bio = TextField(null=True)\n       status = CharField(\n           verbose_name='Status',\n           help_text='Record status',\n           choices=[\n               ('active', 'Active'),\n               ('archived', 'Archived'),\n               ('deleted', 'Deleted'),\n           ])\n       created = DateTimeField(default=datetime.datetime.now)\n\n   # Generate a Pydantic model in one call:\n   UserSchema = to_pydantic(User)\n\n``UserSchema`` is a standard Pydantic ``BaseModel``. You can validate data,\nserialize instances, or populate instances from user data:\n\n.. code-block:: python\n\n   # Validate a dict (e.g. from an HTTP request body).\n   data = UserSchema.model_validate({'name': 'Huey', 'age': 14, 'status': 'active'})\n   print(data.model_dump())\n   # {'name': 'Huey', 'age': 14, 'active': True, 'bio': None, 'score': None,\n   #  'status': 'active', 'created': datetime.datetime(...)}\n\n   # Populate an instance from the validated data.\n   user = User(**validated.dict())\n\n   # Validate directly from a Peewee model instance:\n   huey = User.create(name='Huey', age=14, status='active')\n   data = UserSchema.model_validate(huey)\n\n\nHow field metadata is mapped\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:func:`to_pydantic` reads the metadata you already set on your Peewee fields and\ntranslates it into the Pydantic equivalents:\n\n.. list-table::\n   :header-rows: 1\n   :widths: 25 75\n\n   * - Peewee attribute\n     - Pydantic effect\n   * - ``choices``\n     - The generated field uses a ``Literal`` type restricted to the choice\n       values, and the available choices are appended to the field description.\n   * - ``default`` / ``default=callable``\n     - Sets ``default`` or ``default_factory`` on the Pydantic field so it is\n       not required in input data.\n   * - ``null=True``\n     - Wraps the type in ``Optional[...]`` and defaults to ``None`` when no\n       other default is provided.\n   * - ``verbose_name``\n     - Becomes the ``title`` in the JSON schema.\n   * - ``help_text``\n     - Becomes the ``description`` in the JSON schema.\n\nFields with no default and ``null=False`` (default) are **required** in the\ngenerated Pydantic model.\n\n\nField type mapping\n^^^^^^^^^^^^^^^^^^\n\nPeewee field types are mapped to a Python type that Pydantic uses for\nvalidation.\n\n+-------------------------------------------+------------------------+\n| Peewee field                              | Python type            |\n+===========================================+========================+\n| ``CharField``, ``FixedCharField``,        | ``str``                |\n| ``TextField``                             |                        |\n+-------------------------------------------+------------------------+\n| ``IntegerField``, ``SmallIntegerField``,  | ``int``                |\n| ``BigIntegerField``                       |                        |\n+-------------------------------------------+------------------------+\n| ``AutoField``, ``BigAutoField``           | ``int``                |\n+-------------------------------------------+------------------------+\n| ``FloatField``, ``DoubleField``           | ``float``              |\n+-------------------------------------------+------------------------+\n| ``DecimalField``                          | ``Decimal``            |\n+-------------------------------------------+------------------------+\n| ``BooleanField``                          | ``bool``               |\n+-------------------------------------------+------------------------+\n| ``DateTimeField``                         | ``datetime.datetime``  |\n+-------------------------------------------+------------------------+\n| ``DateField``                             | ``datetime.date``      |\n+-------------------------------------------+------------------------+\n| ``TimeField``                             | ``datetime.time``      |\n+-------------------------------------------+------------------------+\n| ``BlobField``                             | ``bytes``              |\n+-------------------------------------------+------------------------+\n| ``UUIDField``                             | ``uuid.UUID``          |\n+-------------------------------------------+------------------------+\n| ``JSONField``, ``BinaryJSONField``        | ``dict``               |\n| (SQLite or Postgres extensions)           |                        |\n+-------------------------------------------+------------------------+\n| ``IntervalField`` (Postgres)              | ``datetime.timedelta`` |\n+-------------------------------------------+------------------------+\n| ``ForeignKeyField``                       | *type of related PK*   |\n+-------------------------------------------+------------------------+\n\n``AutoField`` and ``BigAutoField`` are excluded from the generated schema by\ndefault (``exclude_autofield=True``) - they can be included by passing\n``exclude_autofield=False``.\n\n``ForeignKeyField`` resolves through the related model's primary-key field, so\na foreign key to a model with an ``AutoField`` PK becomes ``int``. This is\noverridden when you provide a nested schema via the ``relationships``\nparameter.\n\nAny field whose ``field_type`` is not present in the map falls back to\n``Any``, which means Pydantic will accept any value without validation. If you\nuse custom field type and want strict validation, ensure they set a\nrecognized ``field_type`` or handle the conversion yourself.\n\nWhen a field has ``choices`` defined, the mapped Python type above is\n**replaced** by a ``Literal`` constrained to the choice values, regardless of\nthe underlying field type.\n\n\nAPI reference\n^^^^^^^^^^^^^^\n\n.. function:: to_pydantic(model_cls, exclude=None, include=None, exclude_autofield=True, model_name=None, relationships=None)\n\n   Generate a Pydantic ``BaseModel`` class from a Peewee model.\n\n   :param Model model_cls: Peewee model class.\n   :param exclude: Field names to exclude from the generated schema.\n   :type exclude: set or list\n   :param include: If provided, *only* these field names will appear in the\n       generated schema. All other fields are excluded.\n   :type include: set or list\n   :param bool exclude_autofield: When ``True`` (the default), the\n       auto-incrementing primary-key field is omitted from the schema. Set to\n       ``False`` when you need the ``id`` field in responses.\n   :param str model_name: Name for the generated Pydantic class. Defaults to\n       ``<ModelName>Schema``.\n   :param dict relationships: A mapping that tells ``to_pydantic`` how to\n       handle foreign-key or back-reference fields as nested Pydantic models\n       instead of flat scalar values. See :ref:`pydantic-relationships` below.\n   :returns: A Pydantic ``BaseModel`` subclass configured with\n       ``from_attributes=True``.\n\n   Generate a Pydantic ``Model`` for the given Peewee ``model_cls``. The\n   generated model will preserve Peewee field metadata:\n\n   * ``choices`` - restrict acceptable values for field.\n   * ``default`` - provide a default value for field.\n   * ``verbose_name`` - provide a human-readable title for field.\n   * ``help_text`` - provide a human-readable description for field.\n   * ``null`` - control whether field is optional or required.\n\n   Foreign-key fields are exposed using the underlying column name, and\n   accept a scalar value **unless** you specify the schema for the relation\n   using the ``relationships`` parameter. See below for example.\n\n\nForeign-key handling\n^^^^^^^^^^^^^^^^^^^^\n\nBy default, foreign-key fields are exposed using their **underlying column name**\n(e.g. ``user_id`` rather than ``user``) and accept a plain scalar value, typically\nan integer primary key. This keeps the schema flat and is a good fit when you\nare accepting input data:\n\n.. code-block:: python\n\n   class Tweet(db.Model):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(default=datetime.datetime.now)\n       is_published = BooleanField(default=True)\n\n   TweetSchema = to_pydantic(Tweet)\n\n   # The schema exposes the column name \"user_id\", not \"user\":\n   data = TweetSchema.model_validate({'user_id': 1, 'content': 'hello'})\n   print(data.model_dump())\n   # {'user_id': 1,\n   #  'content': 'hello',\n   #  'timestamp': datetime.datetime(...),\n   #  'is_published: True}\n\n   # Works when validating from a model instance too:\n   tweet = Tweet.create(user=huey, content='hello')\n   data = TweetSchema.model_validate(tweet)\n   print(data.model_dump())\n   # {'user_id': 1,\n   #  'content': 'hello',\n   #  'timestamp': datetime.datetime(...),\n   #  'is_published: True}\n\n.. _pydantic-relationships:\n\nNested relationships\n^^^^^^^^^^^^^^^^^^^^\n\nWhen you wish to embed the related object rather than just its ID, pass a\n``relationships`` dict that maps a Peewee :class:`ForeignKeyField`\n(or backref) to the Pydantic schema that should be used for the nested object.\n\n**Nested foreign key**\n\n.. code-block:: python\n\n   # Include the id field so it appears in the response.\n   UserSchema = to_pydantic(User, exclude_autofield=False)\n\n   TweetResponse = to_pydantic(\n       Tweet,\n       exclude_autofield=False,\n       relationships={Tweet.user: UserSchema})\n\n   tweet = Tweet.create(user=huey, content='hello')\n\n   data = TweetResponse.model_validate(tweet)\n   print(data.model_dump())\n   # {'id': 1,\n   #  'content': 'hello',\n   #  'user': {'id': 1, 'name': 'Huey', 'age': 14, ...},\n   #  'timestamp': datetime.datetime(...),\n   #  'is_published': True}\n\n.. note::\n   Validating from a model instance will access ``tweet.user``, which triggers\n   a SELECT query if the relation is not already loaded. To avoid the extra\n   query, use a join:\n\n   .. code-block:: python\n\n      tweet = (Tweet\n               .select(Tweet, User)\n               .join(User)\n               .get())\n      data = TweetResponse.model_validate(tweet)  # No additional query.\n\n**Nested back-references**\n\nBack-references work the same way, but the schema must be wrapped in\n``List[...]`` since back-references may contain 0..n records.\n\n.. code-block:: python\n\n   from typing import List\n\n   # Exclude the \"user\" FK from the tweet schema to avoid circular nesting.\n   TweetResponse = to_pydantic(Tweet, exclude={'user'}, exclude_autofield=False)\n\n   UserDetail = to_pydantic(\n       User,\n       exclude_autofield=False,\n       relationships={User.tweets: List[TweetResponse]})\n\n   user = User.create(name='Huey', age=14, status='active')\n   Tweet.create(user=user, content='tweet 0')\n   Tweet.create(user=user, content='tweet 1')\n\n   data = UserDetail.model_validate(user)\n   print(data.model_dump())\n   # {'id': 1, 'name': 'Huey', ...,\n   #  'tweets': [{'id': 1, 'content': 'tweet 0', ...},\n   #             {'id': 2, 'content': 'tweet 1', ...}]}\n\n.. note::\n   As with foreign keys, accessing a back-reference triggers a query. Use\n   :py:meth:`~ModelSelect.prefetch` to load the collection up front:\n\n   .. code-block:: python\n\n      users = (User\n               .select()\n               .where(User.id == 123)\n               .prefetch(Tweet))\n\n      data = UserDetail.model_validate(users[0])  # No additional query.\n\n\nJSON schema output\n^^^^^^^^^^^^^^^^^^\n\nBecause the generated class is a regular Pydantic model, you can call\n``model_json_schema()`` to get a JSON-schema dict suitable for OpenAPI docs:\n\n.. code-block:: python\n\n   import json\n   print(json.dumps(UserSchema.model_json_schema(), indent=2))\n\n.. code-block:: json\n\n   {\n     \"properties\": {\n       \"name\": {\n         \"description\": \"Display name\",\n         \"title\": \"Full Name\",\n         \"type\": \"string\"\n       },\n       \"age\": {\n         \"title\": \"Age\",\n         \"type\": \"integer\"\n       },\n       \"active\": {\n         \"default\": true,\n         \"title\": \"Active\",\n         \"type\": \"boolean\"\n       },\n       \"bio\": {\n         \"anyOf\": [{\"type\": \"string\"}, {\"type\": \"null\"}],\n         \"default\": null,\n         \"title\": \"Bio\"\n       },\n       \"status\": {\n         \"description\": \"Record status | Choices: 'active' = Active, 'archived' = Archived, 'deleted' = Deleted\",\n         \"enum\": [\"active\", \"archived\", \"deleted\"],\n         \"title\": \"Status\",\n         \"type\": \"string\"\n       },\n       \"created\": {\n         \"format\": \"date-time\",\n         \"title\": \"Created\",\n         \"type\": \"string\"\n       }\n     },\n     \"required\": [\"name\", \"age\", \"status\"],\n     \"title\": \"UserSchema\",\n     \"type\": \"object\"\n   }\n\nNote that ``name``, ``age``, and ``status`` are the only required fields. All\nother fields have defaults (``active`` defaults to ``True``, ``bio`` defaults\nto ``None``, and ``created`` uses a ``default_factory``).\n\n\n.. _hybrid:\n\nHybrid Attributes\n-----------------\n\n.. module:: playhouse.hybrid\n\nA *hybrid attribute* behaves differently depending on whether it is accessed\non a model **instance** (executes Python logic) or on the model **class**\n(generates a SQL expression). This lets you write Python methods that work\nboth as Python computations and as composable SQL clauses.\n\nThe concept is borrowed from SQLAlchemy's `hybrid extension <https://docs.sqlalchemy.org/en/14/orm/extensions/hybrid.html>`_.\n\n.. code-block:: python\n\n   from playhouse.hybrid import hybrid_property, hybrid_method\n\n   class Interval(Model):\n       start = IntegerField()\n       end = IntegerField()\n\n       @hybrid_property\n       def length(self):\n           return self.end - self.start\n\n       @hybrid_method\n       def contains(self, point):\n           return (self.start <= point) & (point < self.end)\n\nOn an instance, Python arithmetic runs:\n\n.. code-block:: python\n\n   i = Interval(start=1, end=5)\n   i.length  # 4 (Python arithmetic)\n   i.contains(3)  # True (Python comparison)\n\nOn the class, SQL is generated:\n\n.. code-block:: python\n\n   Interval.select().where(Interval.length > 5)\n   # WHERE (\"end\" - \"start\") > 5\n\n   Interval.select().where(Interval.contains(2))\n   # WHERE (\"start\" <= 2) AND (2 < \"end\")\n\nWhen the Python and SQL implementations differ, provide a separate\n``expression`` override:\n\n.. code-block:: python\n\n   class Interval(Model):\n       start = IntegerField()\n       end = IntegerField()\n\n       @hybrid_property\n       def radius(self):\n           return abs(self.length) / 2  # Python: uses Python abs()\n\n       @radius.expression\n       def radius(cls):\n           return fn.ABS(cls.length) / 2  # SQL: uses fn.ABS()\n\nExample:\n\n.. code-block:: python\n\n   query = Interval.select().where(Interval.radius < 3)\n\nThis query is equivalent to the following SQL:\n\n.. code-block:: sql\n\n   SELECT \"t1\".\"id\", \"t1\".\"start\", \"t1\".\"end\"\n   FROM \"interval\" AS t1\n   WHERE ((abs(\"t1\".\"end\" - \"t1\".\"start\") / 2) < 3)\n\n.. class:: hybrid_property(fget, fset=None, fdel=None, expr=None)\n\n   Decorator for defining a property with separate instance and class\n   behaviors. Use ``@prop.expression`` to specify the SQL form when it\n   differs from the Python form.\n\n   Examples:\n\n   .. code-block:: python\n\n      class Interval(Model):\n          start = IntegerField()\n          end = IntegerField()\n\n          @hybrid_property\n          def length(self):\n              return self.end - self.start\n\n          @hybrid_property\n          def radius(self):\n              return abs(self.length) / 2\n\n          @radius.expression\n          def radius(cls):\n              return fn.ABS(cls.length) / 2\n\n   When accessed on an ``Interval`` instance, the ``length`` and ``radius``\n   properties will behave as you would expect. When accessed as class\n   attributes, though, a SQL expression will be generated instead:\n\n   .. code-block:: python\n\n      query = (Interval\n               .select()\n               .where(\n                   (Interval.length > 6) &\n                   (Interval.radius >= 3)))\n\n   Would generate the following SQL:\n\n   .. code-block:: sql\n\n      SELECT \"t1\".\"id\", \"t1\".\"start\", \"t1\".\"end\"\n      FROM \"interval\" AS t1\n      WHERE (\n          ((\"t1\".\"end\" - \"t1\".\"start\") > 6) AND\n          ((abs(\"t1\".\"end\" - \"t1\".\"start\") / 2) >= 3)\n      )\n\n.. class:: hybrid_method(func, expr=None)\n\n   Decorator for defining a method with separate instance and class\n   behaviors. Use ``@method.expression`` to specify the SQL form.\n\n   Example:\n\n   .. code-block:: python\n\n      class Interval(Model):\n          start = IntegerField()\n          end = IntegerField()\n\n          @hybrid_method\n          def contains(self, point):\n              return (self.start <= point) & (point < self.end)\n\n   When called with an ``Interval`` instance, the ``contains`` method will\n   behave as you would expect. When called as a classmethod, though, a SQL\n   expression will be generated:\n\n   .. code-block:: python\n\n      query = Interval.select().where(Interval.contains(2))\n\n   Would generate the following SQL:\n\n   .. code-block:: sql\n\n      SELECT \"t1\".\"id\", \"t1\".\"start\", \"t1\".\"end\"\n      FROM \"interval\" AS t1\n      WHERE ((\"t1\".\"start\" <= 2) AND (2 < \"t1\".\"end\"))\n\n\n.. _kv:\n\nKey/Value Store\n---------------\n\n.. module:: playhouse.kv\n\n``playhouse.kv.KeyValue`` provides a persistent dictionary backed by a Peewee\ndatabase instance.\n\n\n.. code-block:: python\n\n   from playhouse.kv import KeyValue\n\n   KV = KeyValue()   # Defaults to an in-memory SQLite database.\n\n   KV['k1'] = 'v1'\n   KV.update(k2='v2', k3='v3')\n\n   assert KV['k2'] == 'v2'\n   print(dict(KV))   # {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}\n\n   # Expression-based access:\n   for value in KV[KV.key > 'k1']:\n       print(value)    # 'v2', 'v3'\n\n   # Expression-based bulk update:\n   KV[KV.key > 'k1'] = 'updated'\n\n   # Expression-based deletion:\n   del KV[KV.key > 'k1']\n\n.. class:: KeyValue(key_field=None, value_field=None, ordered=False, database=None, table_name='keyvalue')\n\n   :param Field key_field: Field for the key. Defaults to\n       :class:`CharField`. Must specify ``primary_key=True``.\n   :param Field value_field: Field for the value. Defaults to\n       :class:`PickleField`.\n   :param bool ordered: Return keys in sorted order when iterating.\n   :param Database database: Database to use. Defaults to an in-memory\n       SQLite database.\n   :param str table_name: Name of the underlying table.\n\n   The table is created automatically on construction if it does not exist.\n   Supports the standard dictionary interface plus expression-based access.\n\n   .. method:: __contains__(expr)\n\n      :param expr: a single key or an expression\n      :returns: Boolean whether key/expression exists.\n\n      Example:\n\n      .. code-block:: python\n\n         kv = KeyValue()\n         kv.update(k1='v1', k2='v2')\n\n         'k1' in kv  # True\n\n         'kx' in kv  # False\n\n         (KV.key < 'k2') in KV  # True\n         (KV.key > 'k2') in KV  # False\n\n   .. method:: __len__()\n\n      :returns: Count of items stored.\n\n   .. method:: __getitem__(expr)\n\n      :param expr: a single key or an expression.\n      :returns: value(s) corresponding to key/expression.\n      :raises: ``KeyError`` if single key given and not found.\n\n      Examples:\n\n      .. code-block:: python\n\n         KV = KeyValue()\n         KV.update(k1='v1', k2='v2', k3='v3')\n\n         KV['k1']  # 'v1'\n         KV['kx']  # KeyError: \"kx\" not found\n\n         KV[KV.key > 'k1']  # ['v2', 'v3']\n         KV[KV.key < 'k1']  # []\n\n   .. method:: __setitem__(expr, value)\n\n      :param expr: a single key or an expression.\n      :param value: value to set for key(s)\n\n      Set value for the given key. If ``expr`` is an expression, then any\n      keys matching the expression will have their value updated.\n\n      Example:\n\n      .. code-block:: python\n\n         KV = KeyValue()\n         KV.update(k1='v1', k2='v2', k3='v3')\n\n         KV['k1'] = 'v1-x'\n         print(KV['k1'])  # 'v1-x'\n\n         KV[KV.key >= 'k2'] = 'v99'\n         print(dict(KV))  # {'k1': 'v1-x', 'k2': 'v99', 'k3': 'v99'}\n\n   .. method:: __delitem__(expr)\n\n      :param expr: a single key or an expression.\n\n      Delete the given key. If an expression is given, delete all keys that\n      match the expression.\n\n      Example:\n\n      .. code-block:: python\n\n         KV = KeyValue()\n         KV.update(k1=1, k2=2, k3=3)\n\n         del KV['k1']  # Deletes \"k1\".\n         del KV['k1']  # KeyError: \"k1\" does not exist\n\n         del KV[KV.key > 'k2']  # Deletes \"k3\".\n         del KV[KV.key > 'k99']  # Nothing deleted, no keys match.\n\n   .. method:: keys()\n\n      :returns: an iterable of all keys in the table.\n\n   .. method:: values()\n\n      :returns: an iterable of all values in the table.\n\n   .. method:: items()\n\n      :returns: an iterable of all key/value pairs in the table.\n\n   .. method:: update(__data=None, **mapping)\n\n      Efficiently bulk-insert or replace the given key/value pairs.\n\n      Example:\n\n      .. code-block:: python\n\n         KV = KeyValue()\n         KV.update(k1=1, k2=2)  # Sets 'k1'=1, 'k2'=2.\n\n         print(dict(KV))  # {'k1': 1, 'k2': 2}\n\n         KV.update(k2=22, k3=3)  # Updates 'k2'->22, sets 'k3'=3.\n\n         print(dict(KV))  # {'k1': 1, 'k2': 22, 'k3': 3}\n\n         KV.update({'k2': -2, 'k4': 4})  # Also can pass a dictionary.\n\n         print(dict(KV))  # {'k1': 1, 'k2': -2, 'k3': 3, 'k4': 4}\n\n   .. method:: get(expr, default=None)\n\n      :param expr: a single key or an expression.\n      :param default: default value if key not found.\n      :returns: value of given key/expr or default if single key not found.\n\n      Get the value at the given key. If the key does not exist, the default\n      value is returned, unless the key is an expression in which case an\n      empty list will be returned.\n\n   .. method:: pop(expr, default=Sentinel)\n\n      :param expr: a single key or an expression.\n      :param default: default value if key does not exist.\n      :returns: value of given key/expr or default if single key not found.\n\n      Get value and delete the given key. If the key does not exist, the\n      default value is returned, unless the key is an expression in which\n      case an empty list is returned.\n\n   .. method:: clear()\n\n      Remove all items from the key-value table.\n\n\n.. _signals:\n\nSignals\n-------\n\n.. module:: playhouse.signals\n\n``playhouse.signals`` adds Django-style model lifecycle signals. Models must\nsubclass ``playhouse.signals.Model`` (not ``peewee.Model``) for hooks to fire.\n\n.. code-block:: python\n\n   from playhouse.signals import Model, post_save\n\n   class MyModel(Model):\n       data = IntegerField()\n       class Meta:\n           database = db\n\n   @post_save(sender=MyModel)\n   def on_save(model_class, instance, created):\n       if created:\n           notify_new(instance)\n\nThe following signals are provided:\n\n``pre_save``\n    Called immediately before an object is saved to the database. Provides an\n    additional keyword argument ``created``, indicating whether the model is being\n    saved for the first time or updated.\n``post_save``\n    Called immediately after an object is saved to the database. Provides an\n    additional keyword argument ``created``, indicating whether the model is being\n    saved for the first time or updated.\n``pre_delete``\n    Called immediately before an object is deleted from the database when :meth:`Model.delete_instance`\n    is used.\n``post_delete``\n    Called immediately after an object is deleted from the database when :meth:`Model.delete_instance`\n    is used.\n``pre_init``\n    Called when a model class is first instantiated\n\n.. warning::\n   Signals fire only through the high-level instance methods\n   (:meth:`~Model.save`, :meth:`~Model.delete_instance`). Bulk\n   operations via :meth:`~Model.insert`, :meth:`~Model.update`, and\n   :meth:`~Model.delete` do not trigger signals because no model instance\n   is involved.\n\nConnecting handlers\n^^^^^^^^^^^^^^^^^^^\n\nWhenever a signal is dispatched, it will call any handlers that have been\nregistered. This allows totally separate code to respond to events like model\nsave and delete.\n\nThe :class:`Signal` class provides a :meth:`~Signal.connect` method,\nwhich takes a callback function and two optional parameters for \"sender\" and\n\"name\". If specified, the \"sender\" parameter should be a single model class\nand allows your callback to only receive signals from that one model class.\nThe \"name\" parameter is used as a convenient alias in the event you wish to\nunregister your signal handler.\n\nExample:\n\n.. code-block:: python\n\n   @post_save(sender=MyModel, name='project.cache_buster')\n   def cache_bust(sender, instance, created):\n       cache.delete(make_cache_key(instance))\n\nOr connect manually:\n\n.. code-block:: python\n\n   def on_delete(sender, instance):\n       audit_log(instance)\n\n   pre_delete.connect(on_delete, sender=MyModel)\n\nDisconnect by name or reference:\n\n.. code-block:: python\n\n   post_save.disconnect(name='project.cache_buster')\n   pre_delete.disconnect(on_delete)\n\nSignal callback signature:\n\n- ``pre_init(sender, instance)``\n- ``pre_save(sender, instance, created)``\n- ``post_save(sender, instance, created)``\n- ``pre_delete(sender, instance)``\n- ``post_delete(sender, instance)``\n\n.. class:: Signal()\n\n   Stores a list of receivers (callbacks) and calls them when the \"send\"\n   method is invoked.\n\n   .. method:: connect(receiver, name=None, sender=None)\n\n      :param callable receiver: a callable that takes at least two parameters,\n          a \"sender\", which is the Model subclass that triggered the signal, and\n          an \"instance\", which is the actual model instance.\n      :param string name: a short alias\n      :param Model sender: if specified, only instances of this model class will\n          trigger the receiver callback.\n\n      Add the receiver to the internal list of receivers, which will be called\n      whenever the signal is sent.\n\n      .. code-block:: python\n\n          from playhouse.signals import post_save\n          from project.handlers import cache_buster\n\n          post_save.connect(cache_buster, name='project.cache_buster')\n\n   .. method:: disconnect(receiver=None, name=None, sender=None)\n\n      :param callable receiver: the callback to disconnect\n      :param string name: a short alias\n      :param Model sender: disconnect model-specific handler.\n\n      Disconnect the given receiver (or the receiver with the given name alias)\n      so that it no longer is called. Either the receiver or the name must be\n      provided.\n\n      .. code-block:: python\n\n         post_save.disconnect(name='project.cache_buster')\n\n    .. method:: send(instance, *args, **kwargs)\n\n       :param instance: a model instance\n\n       Iterates over the receivers and will call them in the order in which\n       they were connected. If the receiver specified a sender, it will only\n       be called if the instance is an instance of the sender.\n\n\n.. _dataset:\n\nDataSet\n-------\n\n.. module:: playhouse.dataset\n\n``playhouse.dataset`` exposes a dict-oriented API for relational data, modeled\nafter the `dataset library <https://dataset.readthedocs.io/>`_. It is useful\nfor quick scripts, data loading, and CSV/JSON import-export.\n\nBasic operations:\n\n.. code-block:: python\n\n   from playhouse.dataset import DataSet\n\n   db = DataSet('sqlite:///data.db')\n\n   # Access a table (created automatically if it doesn't exist):\n   users = db['user']\n\n   # Insert rows with any columns:\n   users.insert(name='Alice', age=30)\n   users.insert(name='Bob', age=25, active=True)  # New column added automatically.\n\n   # Retrieve rows:\n   alice = users.find_one(name='Alice')\n   print(alice)   # {'id': 1, 'name': 'Alice', 'age': 30, 'active': None}\n\n   for user in users:\n       print(user['name'])\n\n   for admin in users.find(active=True):\n       print(admin['name'])  # Bob.\n\n   # Update:\n   users.update(name='Alice', age=31, columns=['name'])  # 'name' is the lookup.\n\n   # Update all records:\n   users.update(admin=False)\n\n   # Delete:\n   users.delete(name='Bob')\n\nExport and import data:\n\n.. code-block:: python\n\n   # Export to JSON:\n   db.freeze(users.all(), format='json', filename='users.json')\n\n   # Export CSV to stdout:\n   db.freeze(users.all(), format='csv', file_obj=sys.stdout)\n\n   # Import from CSV:\n   db.thaw('user', format='csv', filename='import.csv')\n\n   # Import a JSON file to a new table.\n   db.thaw('new_table', format='json', filename='json-data.json')\n\nTransactions:\n\n.. code-block:: python\n\n   # Transactions.\n   with db.transaction() as txn:\n       users.insert(name='Charlie')\n\n       with db.transaction() as nested_txn:\n           table.update(name='Charlie', favorite_orm='sqlalchemy', columns=['name'])\n\n           nested_txn.rollback()  # JK.\n\nIntrospection:\n\n.. code-block:: python\n\n   print(db.tables)\n   # ['new_table', 'user']\n\n   print(db['user'].columns)\n   # ['id', 'age', 'name', 'active', 'admin', 'favorite_orm']\n\n   print(len(db['user']))\n   # 2\n\n\n.. class:: DataSet(url, **kwargs)\n\n   :param url: :ref:`db-url` or a :class:`Database` instance.\n   :param kwargs: additional keyword arguments passed to\n       :meth:`Introspector.generate_models` when introspecting the db.\n\n   .. attribute:: tables\n\n      List of table names in the database (computed dynamically).\n\n   .. method:: __getitem__(table_name)\n\n      Return a :class:`Table` for the given name. Creates the table if\n      it doesn't exist.\n\n   .. method:: query(sql, params=None, commit=True)\n\n      :param str sql: A SQL query.\n      :param list params: Optional parameters for the query.\n      :param bool commit: Whether the query should be committed upon execution.\n      :return: A database cursor.\n\n      Execute the provided query against the database.\n\n   .. method:: transaction()\n\n      Return a context manager representing a transaction.\n\n   .. method:: freeze(query, format='csv', filename=None, file_obj=None, encoding='utf8', iso8601_datetimes=False, base64_bytes=False, **kwargs)\n\n      :param query: A :class:`SelectQuery`, generated using :meth:`~Table.all` or `~Table.find`.\n      :param format: Output format. By default, *csv* and *json* are supported.\n      :param filename: Filename to write output to.\n      :param file_obj: File-like object to write output to.\n      :param str encoding: File encoding.\n      :param bool iso8601_datetimes: Encode datetimes and dates in ISO 8601 format.\n      :param bool base64_bytes: Encode binary data as base64. By default hex\n         is used.\n      :param kwargs: Arbitrary parameters for export-specific functionality.\n\n      Export data to a file.\n\n   .. method:: thaw(table, format='csv', filename=None, file_obj=None, strict=False, encoding='utf8', iso8601_datetimes=False, base64_bytes=False, **kwargs)\n\n      :param str table: The name of the table to load data into.\n      :param format: Input format. By default, *csv* and *json* are supported.\n      :param filename: Filename to read data from.\n      :param file_obj: File-like object to read data from.\n      :param bool strict: Whether to store values for columns that do not already exist on the table.\n      :param str encoding: File encoding.\n      :param bool iso8601_datetimes: Decode datetimes and dates from ISO 8601 format.\n      :param bool base64_bytes: Decode BLOB field-data from base64. By default hex\n         is assumed.\n      :param kwargs: Arbitrary parameters for import-specific functionality.\n\n      Import data from a file into ``table``. If ``strict=False`` (default),\n      new columns are added automatically.\n\n   .. method:: connect()\n               close()\n\n      Open or close the underlying database connection.\n\n.. class:: Table(dataset, name, model_class)\n\n   Provides a high-level API for working with rows in a given table.\n\n   .. attribute:: columns\n\n      List of column names.\n\n   .. attribute:: model_class\n\n      A dynamically-created :class:`Model` class.\n\n   .. method:: insert(**data)\n\n      Insert a row, adding new columns as needed.\n\n   .. method:: update(columns=None, conjunction=None, **data)\n\n      Update the table using the provided data. If one or more columns are\n      specified in the *columns* parameter, then those columns' values in the\n      *data* dictionary will be used to determine which rows to update.\n\n      .. code-block:: python\n\n         # Update all rows.\n         db['users'].update(favorite_orm='peewee')\n\n         # Only update Huey's record, setting his age to 3.\n         db['users'].update(name='Huey', age=3, columns=['name'])\n\n   .. method:: find(**query)\n\n      Return all rows matching equality conditions (all rows if no\n      conditions given).\n\n   .. method:: find_one(**query)\n\n      Return the first matching row, or ``None``.\n\n   .. method:: all()\n\n      Return all rows.\n\n   .. method:: delete(**query)\n\n      Delete matching rows (all rows if no conditions given).\n\n   .. method:: create_index(columns, unique=False)\n\n      Create an index on the given columns:\n\n      .. code-block:: python\n\n          # Create a unique index on the `username` column.\n          db['users'].create_index(['username'], unique=True)\n\n   .. method:: freeze(format='csv', filename=None, file_obj=None, encoding='utf8', iso8601_datetimes=False, base64_bytes=False, **kwargs)\n\n      :param format: Output format. By default, *csv* and *json* are supported.\n      :param filename: Filename to write output to.\n      :param file_obj: File-like object to write output to.\n      :param str encoding: File encoding.\n      :param bool iso8601_datetimes: Encode datetimes and dates in ISO 8601 format.\n      :param bool base64_bytes: Encode binary data as base64. By default hex\n         is used.\n      :param kwargs: Arbitrary parameters for export-specific functionality.\n\n   .. method:: thaw(format='csv', filename=None, file_obj=None, strict=False, encoding='utf8', iso8601_datetimes=False, base64_bytes=False, **kwargs)\n\n      :param format: Input format. By default, *csv* and *json* are supported.\n      :param filename: Filename to read data from.\n      :param file_obj: File-like object to read data from.\n      :param bool strict: Whether to store values for columns that do not already exist on the table.\n      :param str encoding: File encoding.\n      :param bool iso8601_datetimes: Decode datetimes and dates from ISO 8601 format.\n      :param bool base64_bytes: Decode BLOB field-data from base64. By default hex\n         is assumed.\n      :param kwargs: Arbitrary parameters for import-specific functionality.\n\n\n.. _extra-fields:\n\nExtra Field Types\n-----------------\n\n.. module:: playhouse.fields\n\n``playhouse.fields`` provides two general-purpose field types.\n\n.. class:: CompressedField(compression_level=6, algorithm='zlib', **kwargs)\n\n   Stores compressed binary data using ``zlib`` or ``bz2``. Extends\n   :class:`BlobField`; compression and decompression are transparent:\n\n   .. code-block:: python\n\n       from playhouse.fields import CompressedField\n\n       class LogEntry(Model):\n           payload = CompressedField(algorithm='zlib', compression_level=9)\n\n   :param int compression_level: 0-9 (9 is maximum compression).\n   :param str algorithm: ``'zlib'`` or ``'bz2'``.\n\n.. class:: PickleField()\n\n   Stores arbitrary Python objects by pickling them into a :class:`BlobField`.\n\n   .. code-block:: python\n\n       from playhouse.fields import PickleField\n\n       class CachedResult(Model):\n           data = PickleField()\n\n       CachedResult.create(data={'nested': [1, 2, 3]})\n\n\n.. _flask-utils:\n\nFlask Utilities\n---------------\n\n.. module:: playhouse.flask_utils\n\n``playhouse.flask_utils`` simplifies Peewee integration with\n`Flask <https://flask.palletsprojects.com/>`_.\n\nFlaskDB Wrapper\n^^^^^^^^^^^^^^^^\n\n:class:`FlaskDB` handles three boilerplate tasks:\n\n1. Creates a Peewee database instance from Flask's ``app.config``.\n2. Provides a ``Model`` base class whose ``Meta.database`` is wired to the\n   Peewee instance.\n3. Registers ``before_request`` / ``teardown_request`` hooks that open and\n   close a connection for every request.\n\nBasic setup:\n\n.. code-block:: python\n\n   from flask import Flask\n   from playhouse.flask_utils import FlaskDB\n\n   app = Flask(__name__)\n   app.config['DATABASE'] = 'postgresql://postgres:pw@localhost/my_app'\n\n   db_wrapper = FlaskDB(app)\n\n   class User(db_wrapper.Model):\n       username = CharField(unique=True)\n\n   class Tweet(db_wrapper.Model):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n\nAccess the underlying Peewee database:\n\n.. code-block:: python\n\n   peewee_db = db_wrapper.database\n\n   @app.route('/transfer', methods=['POST'])\n   def transfer():\n       with peewee_db.atomic():\n           # ... transactional logic ...\n       return jsonify({'ok': True})\n\nApplication factory pattern:\n\n.. code-block:: python\n\n   db_wrapper = FlaskDB()\n\n   class User(db_wrapper.Model):\n       username = CharField(unique=True)\n\n   def create_app():\n       app = Flask(__name__)\n       app.config['DATABASE'] = 'sqlite:///my_app.db'\n       db_wrapper.init_app(app)\n       return app\n\nConfiguration via dict or a :class:`Database` instance directly:\n\n.. code-block:: python\n\n   # Dictionary-based (uses playhouse.db_url under the hood):\n   app.config['DATABASE'] = {\n       'name': 'my_app',\n       'engine': 'playhouse.pool.PooledPostgresqlDatabase',\n       'user': 'postgres',\n       'max_connections': 32,\n   }\n\n   # Pass a database object:\n   peewee_db = PostgresqlExtDatabase('my_app')\n   db_wrapper = FlaskDB(app, peewee_db)\n\nExcluding routes from connection management:\n\n.. code-block:: python\n\n   app.config['FLASKDB_EXCLUDED_ROUTES'] = ('health_check', 'static')\n\n.. class:: FlaskDB(app=None, database=None)\n\n   :param app: Flask application instance (optional; use ``init_app()`` for\n       the factory pattern).\n   :param database: A database URL string, configuration dictionary, or a\n       :class:`Database` instance.\n\n   .. attribute:: database\n\n      The underlying :class:`Database` instance.\n\n   .. attribute:: Model\n\n      A base :class:`Model` class bound to this database instance.\n\n   .. method:: init_app(app)\n\n      Bind to a Flask application (factory pattern).\n\n\nQuery Helpers\n^^^^^^^^^^^^^\n\n.. function:: get_object_or_404(query_or_model, *query)\n\n   :param query_or_model: Either a :class:`Model` class or a pre-filtered :class:`SelectQuery`.\n   :param query: Peewee filter expressions.\n\n   Retrieve a single object matching the given query, or abort with HTTP\n   404 if no match is found.\n\n   .. code-block:: python\n\n       @app.route('/post/<slug>/')\n       def post_detail(slug):\n           post = get_object_or_404(\n               Post.select().where(Post.published == True),\n               Post.slug == slug)\n           return render_template('post_detail.html', post=post)\n\n.. function:: object_list(template_name, query, context_variable='object_list', paginate_by=20, page_var='page', check_bounds=True, **kwargs)\n\n   Paginate a query and render a template with the results.\n\n   :param str template_name: Template to render.\n   :param query: :class:`SelectQuery` to paginate.\n   :param str context_variable: Template variable name for the page of\n       objects (default: ``'object_list'``).\n   :param int paginate_by: Items per page.\n   :param str page_var: GET parameter name for the page number.\n   :param bool check_bounds: Return 404 for invalid page numbers.\n   :param kwargs: Extra template context variables.\n\n   The template receives:\n\n   - ``object_list`` (or ``context_variable``) - page of objects.\n   - ``page`` - current page number.\n   - ``pagination`` - a :class:`PaginatedQuery` instance.\n\n   .. code-block:: python\n\n       @app.route('/posts/')\n       def post_list():\n           return object_list(\n               'post_list.html',\n               query=Post.select().where(Post.published == True),\n               paginate_by=10)\n\n.. class:: PaginatedQuery(query_or_model, paginate_by, page_var='page', check_bounds=False)\n\n   :param query_or_model: Either a :class:`Model` or a :class:`SelectQuery` instance containing the collection of records you wish to paginate.\n   :param paginate_by: Number of objects per-page.\n   :param page_var: The name of the ``GET`` argument which contains the page.\n   :param check_bounds: Whether to check that the given page is a valid page. If ``check_bounds`` is ``True`` and an invalid page is specified, then a 404 will be returned.\n\n   Helper class to perform pagination based on ``GET`` arguments.\n\n   .. method:: get_page()\n\n      Return the current page number (1-based; defaults to 1).\n\n   .. method:: get_page_count()\n\n      Return the total number of pages.\n\n   .. method:: get_object_list()\n\n      Return the :class:`SelectQuery` for the requested page, with\n      appropriate ``LIMIT`` and ``OFFSET`` applied. Returns a 404 if\n      ``check_bounds=True`` and the page is empty.\n"
  },
  {
    "path": "docs/peewee/pool-snippet.rst",
    "content": "Commonly-used pool implementations:\n\n* :class:`PooledPostgresqlDatabase`\n* :class:`PooledMySQLDatabase`\n* :class:`PooledSqliteDatabase`\n\nAdditional implementations:\n\n* ``playhouse.cysqlite_ext`` - :class:`PooledCySqliteDatabase`\n* ``playhouse.mysql_ext`` - :class:`PooledMariaDBConnectorDatabase`\n* ``playhouse.mysql_ext`` - :class:`PooledMySQLConnectorDatabase`\n* ``playhouse.postgres_ext`` - :class:`PooledPostgresqlExtDatabase`\n* ``playhouse.postgres_ext`` - :class:`PooledPsycopg3Database`\n* ``playhouse.cockroachdb`` - :class:`PooledCockroachDatabase`\n"
  },
  {
    "path": "docs/peewee/postgres.rst",
    "content": ".. _postgresql:\n\nPostgresql\n==========\n\n.. module:: playhouse.postgres_ext\n\nThe ``playhouse.postgres_ext`` module exposes Postgresql-specific field types\nand features that are not available in the standard :class:`PostgresqlDatabase`.\n\n.. contents:: On this page\n   :local:\n   :depth: 1\n\nGetting Started\n---------------\n\nTo get started import the ``playhouse.postgres_ext`` module and use the :class:`PostgresqlExtDatabase`\ndatabase class:\n\n.. code-block:: python\n\n    from playhouse.postgres_ext import *\n\n    db = PostgresqlExtDatabase('peewee_test', user='postgres')\n\n    class BaseExtModel(Model):\n        class Meta:\n            database = db\n\n.. _postgres-ext-api:\n\nPostgresqlExtDatabase\n---------------------\n\n.. class:: PostgresqlExtDatabase(database, server_side_cursors=False, register_hstore=False, prefer_psycopg3=False, **kwargs)\n\n   Extends :class:`PostgresqlDatabase` and is required to use:\n\n   * :class:`ArrayField`\n   * :class:`DateTimeTZField`\n   * :class:`JSONField` / :class:`BinaryJSONField`\n   * :class:`HStoreField`\n   * :class:`TSVectorField`\n   * :ref:`postgres-server-side-cursors`\n\n   :param str database: Name of database to connect to.\n   :param bool server_side_cursors: Whether ``SELECT`` queries should utilize\n       server-side cursors.\n   :param bool register_hstore: Register the hstore extension.\n   :param bool prefer_psycopg3: If both psycopg2 and psycopg3 are installed,\n       instruct Peewee to prefer psycopg3.\n\n   When using ``server_side_cursors`` be sure to wrap your queries with :func:`ServerSide`.\n\n.. class:: PooledPostgresqlExtDatabase(database, **kwargs)\n\n   Connection-pooling variant of :class:`PostgresqlExtDatabase`.\n\n.. class:: Psycopg3Database(database, **kwargs)\n\n   Same as :class:`PostgresqlExtDatabase` but specifies ``prefer_psycopg3=True``.\n\n.. class:: PooledPsycopg3Database(database, **kwargs)\n\n   Connection-pooling variant of :class:`Psycopg3Database`.\n\n.. _postgres-json:\n\nJSON Support\n------------\n\nPeewee provides two JSON field types for Postgresql:\n\n- :class:`BinaryJSONField` - stores JSON in the efficient binary ``jsonb``\n  format. Supports key/item access, containment operations.\n- :class:`JSONField` - stores JSON as text, supports key/item access.\n\nMost applications will wish to use :class:`BinaryJSONField` (``JSONB``):\n\n* Faster Queries: direct access to data elements without parsing the entire\n  JSON document each time.\n* Index Support: supports indexing via GiST or GIN.\n* Faster updates without requiring rewriting the entire document.\n\nThe only time :class:`JSONField` is preferable is when you must store\nthe exact JSON data verbatim (whitespace, object key ordering).\n\n.. code-block:: python\n\n   from playhouse.postgres_ext import PostgresqlExtDatabase, BinaryJSONField\n\n   db = PostgresqlExtDatabase('my_app')\n\n   class Event(Model):\n       data = BinaryJSONField()\n       class Meta:\n           database = db\n\n   # Store data:\n   Event.create(data={\n       'type': 'login',\n       'user_id': 42,\n       'request': {'ip': '1.2.3.4'},\n       'success': True})\n\n   # Filter using a nested key:\n   query = (Event\n            .select()\n            .where(Event.data['request']['ip'] == '1.2.3.4'))\n\n   # Select, group and order-by JSON values.\n   query = (Event\n            .select(Event.data['user_id'],\n                    fn.COUNT(Event.id))\n            .group_by(Event.data['user_id'])\n            .order_by(Event.data['user_id'])\n            .tuples())\n\n   # Retrieve JSON objects.\n   query = (Event\n            .select(Event.data['request'].as_json().alias('request'))\n            .where(Event.data['user_id'] == 42))\n   for event in query:\n       print(event.request['ip'])\n\n.. tip::\n   Refer to the `Postgresql JSON documentation <https://www.postgresql.org/docs/current/functions-json.html>`__\n   for in-depth discussion and examples of using JSON and JSONB.\n\nBinaryJSONField and JSONField\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. class:: BinaryJSONField(dumps=None, *args, **kwargs)\n\n   :param dumps: custom implementation of ``json.dumps``\n\n   Extends :class:`JSONField` for the ``jsonb`` type.\n\n   By default BinaryJSONField will use a GiST index. To disable this,\n   initialize the field with ``index=False``.\n\n   .. method:: as_json()\n\n      Deserialize and return the JSON value at the given path.\n\n   .. method:: concat(data)\n\n      Concatenate the field value with ``data``. Note this is a shallow\n      operation and does not deep-merge nested objects.\n\n      Example:\n\n      .. code-block:: python\n\n         # Add object - if \"result\" key existed before it is overwritten.\n         (Event\n          .update(data=Event.data.concat({'result': {'success': True}}))\n          .execute())\n\n      Nested data can also use ``concat()``:\n\n      .. code-block:: python\n\n         # Select the result subkey and merge with additional data:\n         # {'ip': '1.2.3.4'} --> {'ip': '1.2.3.4', 'status': 'ok'}\n\n         Event.select(Event.data['result'].concat({'status': 'ok'}))\n\n   .. method:: contains(other)\n\n      Test whether this field's value contains ``other`` (as a subset).\n      ``other`` may be a partial dict, list, or scalar value. Useful for\n      matching against a partial JSON object or checking if an item is in an\n      array.\n\n      .. code-block:: python\n\n         Event.create(data={\n             'type': 'rename',\n             'name': 'new name',\n             'metadata': {'old_name': 'the old name'},\n             'tags': ['t1', 't2', 't3']})\n\n         # These queries match the above row:\n\n         # Search by partial object:\n         Event.select().where(Event.data.contains({'type': 'rename'}))\n\n         # Partial object and partial array:\n         Event.select().where(Event.data.contains({\n             'type': 'rename',\n             'tags': ['t2', 't1'],\n         }))\n\n         # Partial array, irrespective of ordering:\n         Event.select().where(Event.data['tags'].contains(['t2', 't1']))\n\n         # Search array by individual item:\n         Event.select().where(Event.data['tags'].contains('t1'))\n\n      To test whether a **key** simply exists, use :meth:`~BinaryJSONField.has_key`:\n\n      .. code-block:: python\n\n         Event.select().where(Event.data.has_key('name'))\n\n         # Or search a sub-key.\n         Event.select().where(Event.data['metadata'].has_key('old_name'))\n\n   .. method:: contains_any(*keys)\n\n      Test whether any of ``keys`` is present in the JSON value.\n\n      .. code-block:: python\n\n         Event.create(data={\n             'type': 'rename',\n             'name': 'new name',\n             'metadata': {'old_name': 'the old name'},\n             'tags': ['t1', 't2', 't3']})\n\n         # These queries match the above row:\n\n         Event.select().where(Event.data.contains_any('name', 'other'))\n\n         # Search a nested object:\n         Event.select().where(\n             Event.data['metadata'].contains_any('old_name', 'old_status'))\n\n         # Search nested object for items in an array:\n         Event.select().where(Event.data['tags'].contains_any('t3', 'tx'))\n\n   .. method:: contains_all(*keys)\n\n      Test whether all of ``keys`` are present in the JSON value.\n\n      .. code-block:: python\n\n         Event.create(data={\n             'type': 'rename',\n             'name': 'new name',\n             'metadata': {'old_name': 'the old name'},\n             'tags': ['t1', 't2', 't3']})\n\n         # These queries match the above row:\n\n         Event.select().where(Event.data.contains_all('name', 'tags'))\n\n         # Search nested object for items in an array:\n         Event.select().where(Event.data['tags'].contains_all('t3', 't2'))\n\n   .. method:: contained_by(other)\n\n      Test whether this field's value is a subset of ``other``.\n\n      .. code-block:: python\n\n         Event.create(data={\n             'type': 'login',\n             'result': {'success': True}})\n\n         Event.create(data={\n             'type': 'rename',\n             'name': 'new name',\n             'metadata': {'old_name': 'the old name'},\n             'tags': ['t1', 't2', 't3']})\n\n         # Matches the login row.\n         (Event\n          .select()\n          .where(Event.data.contained_by({\n              'type': 'login',\n              'result': {'success': True, 'message': 'OK'}})))\n\n         # Match events that have a result w/success=True and/or\n         # error=False:\n         Event.select().where(Event.data['result'].contained_by({\n             'success': True,\n             'error': False})\n\n         # Check that tags are subset of the popular tags (matches rename row).\n         popular_tags = ['t3', 't2', 't1', 'tx', 'ty']\n         Event.select().where(Event.data['tags'].contained_by(popular_tags))\n\n   .. method:: has_key(key)\n\n      Test whether ``key`` exists.\n\n      .. code-block:: python\n\n         Event.select().where(Event.data.has_key('result'))\n\n         Event.select().where(Event.data['result'].has_key('success'))\n\n   .. method:: remove(*keys)\n\n      Remove one or more keys from the JSON object.\n\n      .. code-block:: python\n\n         # Atomically remove key:\n         Event.update(data=Event.data.remove('result')).execute()\n\n         # Equivalent to above:\n         Event.update(data=Event.data['result'].remove()).execute()\n\n         # Remove deeply-nested item:\n         Event.update(data=Event.data['metadata']['prior'].remove())\n\n   .. method:: length()\n\n      Return the length of the JSON array at the given path.\n\n      .. code-block:: python\n\n         Event.select().where(Event.data['tags'].length() > 3)\n\n   .. method:: extract(*path)\n\n      Extract the JSON data at the given path.\n\n      .. code-block:: python\n\n         Event.select().where(Event.data.extract('tags', 0) == 'first_tag')\n\n         Event.select().where(Event.data.extract('result', 'success') == True)\n\n         # Equivalent to above.\n         Event.select().where(Event.data['result'].extract('success') == True)\n\n\n.. class:: JSONField(dumps=None, *args, **kwargs)\n\n   :param dumps: custom implementation of ``json.dumps``\n\n   Field that stores and retrieves JSON data. Supports ``__getitem__`` key\n   access for filtering and sub-object retrieval.\n\n   Consider using the :class:`BinaryJSONField` instead as it\n   offers better performance and more powerful querying options.\n\n   .. method:: as_json()\n\n      Deserialize and return the JSON value at the given path.\n\n   .. method:: concat(data)\n\n      Concatenate the field value with ``data``. Note this is a shallow\n      operation and does not deep-merge nested objects.\n\n      See :meth:`BinaryJSONField.concat` for example usage.\n\n   .. method:: length()\n\n      Return the length of the JSON array at the given path.\n\n      See :meth:`BinaryJSONField.length` for example usage.\n\n   .. method:: extract(*path)\n\n      Extract the JSON data at the given path.\n\n      See :meth:`BinaryJSONField.extract` for example usage.\n\n\n.. _postgres-hstore:\n\nHStore\n------\n\nPostgresql's `hstore <https://www.postgresql.org/docs/current/hstore.html>`_\nextension stores arbitrary key/value pairs in a single column. Enable it by\npassing ``register_hstore=True`` when initializing the database:\n\n.. code-block:: python\n\n   db = PostgresqlExtDatabase('my_app', register_hstore=True)\n\n   class Event(Model):\n       data = HStoreField()\n       class Meta:\n           database = db\n\n:class:`HStoreField` supports the following operations:\n\n* Store and retrieve arbitrary dictionaries\n* Filter by key(s) or partial dictionary\n* Update/add one or more keys to an existing dictionary\n* Delete one or more keys from an existing dictionary\n* Select keys, values, or zip keys and values\n* Retrieve a slice of keys/values\n* Test for the existence of a key\n* Test that a key has a non-NULL value\n\nExample:\n\n.. code-block:: python\n\n   # Create a record with arbitrary attributes:\n   Event.create(data={\n       'type': 'register',\n       'ip': '1.2.3.4',\n       'email': 'charles@example.com',\n       'result': 'success',\n       'referrer': 'google.com'})\n\n   Event.create(data={\n       'type': 'login',\n       'ip': '1.2.3.4',\n       'email': 'charles@example.com',\n       'result': 'success'})\n\n   # Lookup nested values in the data:\n   Event.select().where(Event.data['type'] == 'login')\n\n   # Filter by a key/value pair:\n   Event.select().where(Event.data.contains({'result': 'success'})\n\n   # Filter by key existence:\n   Event.select().where(Event.data.exists('referrer'))\n\n   # Atomic update - adds new keys, updates existing ones:\n   new_data = Event.data.update({\n       'result': 'ok',\n       'status': 'success'})\n   (Event\n    .update(data=new_data)\n    .where(Event.data['result'] == 'success')\n    .execute())\n\n   # Atomic key deletion:\n   (Event\n    .update(data=Event.data.delete('referrer'))\n    .where(Event.data['referrer'] == 'google.com')\n    .execute())\n\n   # Retrieve keys or values as a list:\n   for event in Event.select(Event.id, Event.data.keys().alias('k')):\n       print(event.id, event.k)\n\n   # Prints:\n   # 1 ['ip', 'type', 'email', 'result', 'status']\n\n   # Retrieve a subset of data:\n   query = (Event\n            .select(Event.id,\n                    Event.data.slice('ip', 'email').alias('source'))\n            .order_by(Event.data['ip']))\n   for event in query:\n       print(event.id, event.source)\n\n   # Prints:\n   # 1 {'ip': '1.2.3.4', 'email': 'charles@example.com'}\n\nHStoreField API\n^^^^^^^^^^^^^^^\n\n.. class:: HStoreField()\n\n   By default ``HStoreField`` will use a *GiST* index. To disable this,\n   initialize the field with ``index=False``.\n\n   .. method:: __getitem__(key)\n\n      :param str key: get value at given key.\n\n      Example:\n\n      .. code-block:: python\n\n         Event.select().where(Event.data['type'] == 'login')\n\n   .. method:: contains(value)\n\n      :param value: value to search for.\n      :type value: dict, list, tuple or string key.\n\n      Test whether the HStore data contains the given ``dict`` (match keys and\n      values), ``list``/``tuple`` (match keys), or ``str`` key.\n\n      Example:\n\n      .. code-block:: python\n\n         # Contains key/value pairs:\n         Event.select().where(Event.data.contains({'result': 'success'}))\n\n         # Contains a list of keys:\n         Event.select().where(Event.data.contains(['result', 'status']))\n\n         # Contains a single key:\n         Event.select().where(Event.data.contains('result'))\n\n   .. method:: contains_any(*keys)\n\n      Test whether the HStore contains any of the given keys.\n\n   .. method:: exists(key)\n\n      Test whether key exists in data.\n\n   .. method:: defined(key)\n\n      Test whether key is non-NULL in data.\n\n   .. method:: update(__data=None, **data)\n\n      :param dict __data: Specify update as a ``dict``.\n      :param data: Specify update as keyword arguments.\n\n      Perform an in-place, atomic update.\n\n      .. code-block:: python\n\n         # Atomic update - adds new keys, updates existing ones:\n         new_data = Event.data.update({\n             'result': 'ok',\n             'status': 'success'})\n         (Event\n          .update(data=new_data)\n          .where(Event.data['result'] == 'success')\n          .execute())\n\n   .. method:: delete(*keys)\n\n      :param keys: one or more keys to delete from data.\n\n      .. code-block:: python\n\n         # Atomic key deletion:\n         (Event\n          .update(data=Event.data.delete('referrer'))\n          .where(Event.data['referrer'] == 'google.com')\n          .execute())\n\n   .. method:: slice(*keys)\n\n      :param str keys: keys to retrieve.\n\n      Retrieve only the provided key/value pairs:\n\n      .. code-block:: python\n\n         query = (Event\n                  .select(Event.id,\n                          Event.data.slice('ip', 'email').alias('source'))\n                  .order_by(Event.data['ip']))\n         for event in query:\n             print(event.id, event.source)\n\n         # 1 {'ip': '1.2.3.4', 'email': 'charles@example.com'}\n\n   .. method:: keys()\n\n      Return the keys as a list.\n\n      .. code-block:: python\n\n         query = Event.select(Event.data.keys().alias('keys'))\n         for event in query:\n             print(event.keys)\n\n         # ['ip', 'type', 'email', 'result', 'status']\n\n   .. method:: values()\n\n      Return the values as a list.\n\n   .. method:: items()\n\n      Return the key-value pairs as a 2-dimensional list.\n\n      .. code-block:: python\n\n         query = Event.select(Event.data.items().alias('items'))\n         for event in query:\n             print(event.items)\n\n         # [['ip', '1.2.3.4'],\n         #  ['type', 'register'],\n         #  ['email', 'charles@example.com'],\n         #  ['result', 'ok'],\n         #  ['status', 'success']]\n\n.. _postgres-arrays:\n\nArrays\n------\n\n.. class:: ArrayField(field_class=IntegerField, field_kwargs=None, dimensions=1, convert_values=False)\n\n   Stores a Postgresql array of the given field type.\n\n   :param field_class: a subclass of :class:`Field`, e.g. :class:`IntegerField`.\n   :param dict field_kwargs: arguments to initialize ``field_class``.\n   :param int dimensions: Number of array dimensions.\n   :param bool convert_values: Apply ``field_class`` value conversion to\n       retrieved data.\n\n   By default ArrayField will use a GIN index. To disable this, initialize\n   the field with ``index=False``.\n\n   Example:\n\n   .. code-block:: python\n\n      class Post(Model):\n          tags = ArrayField(CharField)\n\n      Post.create(tags=['python', 'peewee', 'postgresql'])\n      Post.create(tags=['python', 'sqlite'])\n\n      # Get an item by index.\n      Post.select(Post.tags[0].alias('first_tag'))\n\n      # Get a slice:\n      Post.select(Post.tags[:2].alias('first_two'))\n\n   Multi-dimensional array example:\n\n   .. code-block:: python\n\n      class Outline(Model):\n          points = ArrayField(IntegerField, dimensions=2)\n\n      Outline.create(points=[[1, 1], [1, 5], [5, 5], [5, 1]])\n\n   .. method:: contains(*items)\n\n      Filter rows where the array contains all of the given values.\n\n      :param items: One or more items that must be in the given array field.\n\n      .. code-block:: python\n\n         Post.select().where(Post.tags.contains('postgresql', 'python'))\n\n   .. method:: contains_any(*items)\n\n      Filter rows where the array contains any of the given values.\n\n      :param items: One or more items to search for in the given array field.\n\n      .. code-block:: python\n\n         Post.select().where(Post.tags.contains('postgresql', 'python'))\n\n.. _postgres-interval:\n\nInterval\n--------\n\n.. class:: IntervalField(**kwargs)\n\n   Stores Python ``datetime.timedelta`` instances using Postgresql's native\n   ``INTERVAL`` type.\n\n   .. code-block:: python\n\n      from datetime import timedelta\n\n      class Subscription(Model):\n          duration = IntervalField()\n\n      Subscription.create(duration=timedelta(days=30))\n\n      (Subscription\n       .select()\n       .where(Subscription.duration > timedelta(days=10)))\n\n.. _postgres-datetimetz:\n\nDateTimeTZ Field\n-----------------\n\n.. class:: DateTimeTZField(**kwargs)\n\n   Timezone-aware datetime field using Postgresql's ``TIMESTAMP WITH TIME ZONE``\n   type.\n\n   .. code-block:: python\n\n      class Event(Model):\n          timestamp = DateTimeTZField()\n\n      now = datetime.datetime.now().astimezone(datetime.timezone.utc)\n\n      Event.create(timestamp=now)\n\n      event = Event.get()\n      print(event.timestamp)\n      # 2026-01-02 03:04:05.012345+00:00\n\n.. _postgres-fts:\n\nFull-Text Search\n----------------\n\nPostgresql full-text search uses the ``tsvector`` and ``tsquery`` types.\nPeewee offers two approaches: the simple :func:`Match` function (no schema\nchanges required) and the :class:`TSVectorField` for dedicated search columns\n(better performance).\n\n**Simple approach** - no schema changes required:\n\n.. code-block:: python\n\n   from playhouse.postgres_ext import Match\n\n   def search_posts(term):\n       return Post.select().where(\n           (Post.status == 'published') &\n           Match(Post.body, term))\n\nThe :func:`Match` function will automatically convert the left-hand operand\nto a ``tsvector``, and the right-hand operand to a ``tsquery``. For better\nperformance, create a ``GIN`` index:\n\n.. code-block:: sql\n\n   CREATE INDEX posts_fts ON post USING gin(to_tsvector('english', body));\n\n**Dedicated column** - better performance:\n\n.. code-block:: python\n\n   class Post(Model):\n       body = TextField()\n       search_content = TSVectorField()  # Automatically gets a GIN index.\n\n   # Store a post and populate the search vector:\n   Post.create(\n       body=body_text,\n       search_content=fn.to_tsvector(body_text))\n\n   # Search:\n   Post.select().where(Post.search_content.match('python postgresql'))\n\n   # Search using expressions:\n   terms = 'python & (sqlite | postgres)'\n   Post.select().where(Post.search_content.match(terms))\n\nFor more information, see the `Postgres full-text search docs <https://www.postgresql.org/docs/current/textsearch.html>`_.\n\n.. function:: Match(field, query)\n\n   Generate a full-text search expression that converts ``field`` to\n   ``tsvector`` and ``query`` to ``tsquery`` automatically.\n\n\n.. class:: TSVectorField()\n\n   Field type for storing pre-computed ``tsvector`` data. Automatically\n   created with a GIN index (use ``index=False`` to disable).\n\n   Data must be explicitly converted to ``tsvector`` on write using\n   ``fn.to_tsvector()``.\n\n   Example:\n\n   .. code-block:: python\n\n      class Post(Model):\n          body = TextField()\n          search_content = TSVectorField()\n\n      Post.create(\n          body=body_text,\n          search_content=fn.to_tsvector(body_text))\n\n      (Post\n       .select()\n       .where(Post.search_content.match('python & (sqlite | postgres)')))\n\n   .. method:: match(query, language=None, plain=False)\n\n      :param str query: Full-text search query.\n      :param str language: Optional language name.\n      :param bool plain: Use the plain (simple) query parser instead of the\n          default one, which supports ``&``, ``|``, and ``!`` operators.\n\n.. _postgres-server-side-cursors:\n\nServer-Side Cursors\n-------------------\n\nFor large result sets, server-side (named) cursors stream rows from the server\nrather than loading the entire result into memory. Rows are fetched\ntransparently from the server as you iterate.\n\nRefer to your driver documentation for details:\n\n* `psycopg2 server-side cursors <https://www.psycopg.org/docs/usage.html#server-side-cursors>`__\n* `psycopg3 server-side cursors <https://www.psycopg.org/psycopg3/docs/advanced/cursors.html#server-side-cursors>`__\n\nTo use server-side (or named) cursors, you must be using :class:`PostgresqlExtDatabase`.\n\nWrap any SELECT query with :func:`ServerSide`:\n\n.. code-block:: python\n\n   from playhouse.postgres_ext import ServerSide\n\n   # Must be in a transaction to use server-side cursors.\n   with db.atomic():\n\n       # Create a normal SELECT query.\n       large_query = PageView.select()\n\n       # Then wrap in `ServerSide` and iterate.\n       for page_view in ServerSide(large_query):\n           # Do something interesting.\n           pass\n\n       # At this point server side resources are released.\n\nFor more granular control or to close the cursor explicitly:\n\n.. code-block:: python\n\n   with db.atomic():\n       large_query = PageView.select().order_by(PageView.id.desc())\n\n       # Rows will be fetched 1000 at-a-time, but iteration is transparent.\n       query = ServerSideQuery(query, array_size=1000)\n\n       # Read 9500 rows then close server-side cursor.\n       accum = []\n       for i, obj in enumerate(query.iterator()):\n           if i == 9500:\n               break\n           accum.append(obj)\n\n       # Release server-side resource.\n       query.close()\n\n.. warning::\n   Server-side cursors live only within a transaction. If you are using psycopg2\n   (not psycopg3), cursors are declared ``WITH HOLD`` and must be fully\n   exhausted or explicitly closed to release server resources.\n\n.. function:: ServerSide(select_query)\n\n   :param select_query: a :class:`SelectQuery` instance.\n   :rtype generator:\n\n   Wrap ``select_query`` in a transaction and iterate using :meth:`~BaseQuery.iterator`\n   (disables row caching).\n\n.. _crdb:\n\nCockroachDB\n-----------\n\n.. module:: playhouse.cockroachdb\n\n`CockroachDB <https://www.cockroachlabs.com>`_ (CRDB) is compatible with\nPostgresql's wire protocol and is well-supported by Peewee. Use the dedicated\n:class:`CockroachDatabase` class rather than :class:`PostgresqlDatabase`\nto get CRDB-specific handling.\n\n.. code-block:: python\n\n   from playhouse.cockroachdb import CockroachDatabase\n\n   db = CockroachDatabase('my_app', user='root', host='10.8.0.1')\n\nIf you are using `Cockroach Cloud <https://cockroachlabs.cloud/>`_, you may\nfind it easier to specify the connection parameters using a connection-string:\n\n.. code-block:: python\n\n   db = CockroachDatabase('postgresql://root:secret@host:26257/defaultdb...')\n\nSSL configuration:\n\n.. code-block:: python\n\n   db = CockroachDatabase(\n       'my_app',\n       user='root',\n       host='10.8.0.1',\n       sslmode='verify-full',\n       sslrootcert='/path/to/root.crt')\n\n   # Or, alternatively, specified as part of a connection-string:\n   db = CockroachDatabase('postgresql://root:secret@host:26257/dbname'\n                          '?sslmode=verify-full&sslrootcert=/path/to/root.crt'\n                          '&options=--cluster=my-cluster-xyz')\n\nKey differences from Postgresql\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n* **No nested transactions.** CRDB does not support savepoints, so calling\n  :meth:`~Database.atomic` inside another ``atomic()`` block raises an\n  exception. Use :meth:`~Database.transaction` instead, which ignores\n  nested calls and commits only when the outermost block exits.\n* **Client-side retries.** CRDB may abort transactions due to contention.\n  Use :meth:`~CockroachDatabase.run_transaction` for automatic retries.\n\nSpecial field-types that may be useful when using CRDB:\n\n* :class:`~playhouse.cockroachdb.UUIDKeyField` - a primary-key field implementation that uses\n  CRDB's ``UUID`` type with a default randomly-generated UUID.\n* :class:`~playhouse.cockroachdb.RowIDField` - a primary-key field implementation that uses CRDB's\n  ``INT`` type with a default ``unique_rowid()``.\n* :class:`~playhouse.postgres_ext.JSONField` - same as the Postgres :class:`~playhouse.postgres_ext.BinaryJSONField`, as\n  CRDB treats all JSON as JSONB.\n* :class:`~playhouse.postgres_ext.ArrayField` - same as the Postgres extension (but does not support\n  multi-dimensional arrays).\n\nTransactions:\n\n.. code-block:: python\n\n   # transaction() is safe to nest; the outer block manages the commit.\n   @db.transaction()\n   def create_user(username):\n       return User.create(username=username)\n\n   with db.transaction():\n       create_user('alice')   # Nested call is folded into outer transaction.\n       create_user('bob')\n   # Transaction is committed here.\n\nClient-side retries:\n\n.. code-block:: python\n\n   from playhouse.cockroachdb import CockroachDatabase\n\n   db = CockroachDatabase('my_app')\n\n   def transfer_funds(from_id, to_id, amt):\n       \"\"\"\n       Returns a 3-tuple of (success?, from balance, to balance). If there are\n       not sufficient funds, then the original balances are returned.\n       \"\"\"\n       def thunk(db_ref):\n           src, dest = (Account\n                        .select()\n                        .where(Account.id.in_([from_id, to_id])))\n           if src.id != from_id:\n               src, dest = dest, src  # Swap order.\n\n           # Cannot perform transfer, insufficient funds!\n           if src.balance < amt:\n               return False, src.balance, dest.balance\n\n           # Update each account, returning the new balance.\n           src, = (Account\n                   .update(balance=Account.balance - amt)\n                   .where(Account.id == from_id)\n                   .returning(Account.balance)\n                   .execute())\n           dest, = (Account\n                    .update(balance=Account.balance + amt)\n                    .where(Account.id == to_id)\n                    .returning(Account.balance)\n                    .execute())\n           return True, src.balance, dest.balance\n\n       # Perform the queries that comprise a logical transaction. In the\n       # event the transaction fails due to contention, it will be auto-\n       # matically retried (up to 10 times).\n       return db.run_transaction(thunk, max_attempts=10)\n\nCRDB API\n^^^^^^^^^\n\n.. class:: CockroachDatabase(database, **kwargs)\n\n   Subclass of :class:`PostgresqlDatabase` for CockroachDB.\n\n   .. method:: run_transaction(callback, max_attempts=None, system_time=None, priority=None)\n\n      :param callback: Callable accepting a single ``db`` argument.\n          Must not manage the transaction itself. May be called multiple times.\n      :param int max_attempts: Retry limit.\n      :param datetime system_time: Execute ``AS OF SYSTEM TIME`` with respect\n          to the given value.\n      :param str priority: ``'low'``, ``'normal'``, or ``'high'``.\n      :raises ExceededMaxAttempts: When ``max_attempts`` is exceeded.\n\n      Execute SQL in a transaction with automatic client-side retries.\n\n      User-provided ``callback``:\n\n      * **Must** accept one parameter, the ``db`` instance representing the\n        connection the transaction is running under.\n      * **Must** not attempt to commit, rollback or otherwise manage the\n        transaction.\n      * **May** be called more than one time.\n      * **Should** ideally only contain SQL operations.\n\n      Additionally, the database must not have any open transactions at the\n      time this function is called, as CRDB does not support nested\n      transactions. Attempting to do so will raise a ``NotImplementedError``.\n\n\n.. class:: PooledCockroachDatabase(database, **kwargs)\n\n   Connection-pooling variant of :class:`CockroachDatabase`.\n\n\n.. function:: run_transaction(db, callback, max_attempts=None, system_time=None, priority=None)\n\n    Run SQL in a transaction with automatic client-side retries. See\n    :meth:`CockroachDatabase.run_transaction` for details.\n\n    This function is equivalent to the identically-named method on\n    the :class:`CockroachDatabase` class.\n\n\nCRDB-specific field types:\n\n.. class:: UUIDKeyField()\n   :noindex:\n\n   UUID primary key auto-populated with CRDB's ``gen_random_uuid()``.\n\n.. class:: RowIDField()\n   :noindex:\n\n   Integer primary key auto-populated with CRDB's ``unique_rowid()``.\n"
  },
  {
    "path": "docs/peewee/query_builder.rst",
    "content": ".. _query-builder:\n\nQuery Builder\n=============\n\nPeewee's high-level :class:`Model` and :class:`Field` APIs are built upon\nlower-level :class:`Table` and :class:`Column` counterparts. While these\nlower-level APIs are not documented in as much detail as their high-level\ncounterparts, this document will present an overview with examples that should\nhopefully allow you to experiment.\n\nWe'll use the following schema:\n\n.. code-block:: sql\n\n    CREATE TABLE \"person\" (\n        \"id\" INTEGER NOT NULL PRIMARY KEY,\n        \"first\" TEXT NOT NULL,\n        \"last\" TEXT NOT NULL);\n\n    CREATE TABLE \"note\" (\n        \"id\" INTEGER NOT NULL PRIMARY KEY,\n        \"person_id\" INTEGER NOT NULL,\n        \"content\" TEXT NOT NULL,\n        \"timestamp\" DATETIME NOT NULL,\n        FOREIGN KEY (\"person_id\") REFERENCES \"person\" (\"id\"));\n\n    CREATE TABLE \"reminder\" (\n        \"id\" INTEGER NOT NULL PRIMARY KEY,\n        \"note_id\" INTEGER NOT NULL,\n        \"alarm\" DATETIME NOT NULL,\n        FOREIGN KEY (\"note_id\") REFERENCES \"note\" (\"id\"));\n\nDeclaring tables\n----------------\n\nThere are two ways we can declare :class:`Table` objects for working with\nthese tables:\n\n.. code-block:: python\n\n    # Explicitly declare columns\n    Person = Table('person', ('id', 'first', 'last'))\n\n    Note = Table('note', ('id', 'person_id', 'content', 'timestamp'))\n\n    # Do not declare columns, they will be accessed using magic \".c\" attribute\n    Reminder = Table('reminder')\n\nTypically we will want to :meth:`~Table.bind` our tables to a database. This\nsaves us having to pass the database explicitly every time we wish to execute a\nquery on the table:\n\n.. code-block:: python\n\n    db = SqliteDatabase('my_app.db')\n    Person = Person.bind(db)\n    Note = Note.bind(db)\n    Reminder = Reminder.bind(db)\n\nSelect queries\n--------------\n\nTo select the first three notes and print their content, we can write:\n\n.. code-block:: python\n\n    query = Note.select().order_by(Note.timestamp).limit(3)\n    for note_dict in query:\n        print(note_dict['content'])\n\n.. note::\n    By default, rows will be returned as dictionaries. You can use the\n    :meth:`~BaseQuery.tuples`, :meth:`~BaseQuery.namedtuples` or\n    :meth:`~BaseQuery.objects` methods to specify a different container for\n    the row data, if you wish.\n\nBecause we didn't specify any columns, all the columns we defined in the\nnote's :class:`Table` constructor will be selected. This won't work for\nReminder, as we didn't specify any columns at all.\n\nTo select all notes published in 2018 along with the name of the creator, we\nwill use :meth:`~Select.join`. We'll also request that rows be returned\nas *namedtuple* objects:\n\n.. code-block:: python\n\n    query = (Note\n             .select(Note.content, Note.timestamp, Person.first, Person.last)\n             .join(Person, on=(Note.person_id == Person.id))\n             .where(Note.timestamp >= datetime.date(2018, 1, 1))\n             .order_by(Note.timestamp)\n             .namedtuples())\n\n    for row in query:\n        print(row.timestamp, '-', row.content, '-', row.first, row.last)\n\nLet's query for the most prolific people, that is, get the people who have\ncreated the most notes. This introduces calling a SQL function (COUNT), which\nis accomplished using the ``fn`` object:\n\n.. code-block:: python\n\n    name = Person.first.concat(' ').concat(Person.last)\n    query = (Person\n             .select(name.alias('name'), fn.COUNT(Note.id).alias('count'))\n             .join(Note, JOIN.LEFT_OUTER, on=(Note.person_id == Person.id))\n             .group_by(name)\n             .order_by(fn.COUNT(Note.id).desc()))\n    for row in query:\n        print(row['name'], row['count'])\n\nThere are a couple things to note in the above query:\n\n* We store an expression in a variable (``name``), then use it in the query.\n* We call SQL functions using ``fn.<function>(...)`` passing arguments as if\n  it were a normal Python function.\n* The :meth:`~ColumnBase.alias` method is used to specify the name used for\n  a column or calculation.\n\nAs a more complex example, we'll generate a list of all people and the contents\nand timestamp of their most recently-published note. To do this, we will end up\nusing the Note table twice in different contexts within the same query, which\nwill require us to use a table alias.\n\n.. code-block:: python\n\n    # Start with the query that calculates the timestamp of the most recent\n    # note for each person.\n    NA = Note.alias('na')\n    max_note = (NA\n                .select(NA.person_id, fn.MAX(NA.timestamp).alias('max_ts'))\n                .group_by(NA.person_id)\n                .alias('max_note'))\n\n    # Now we'll select from the note table, joining on both the subquery and\n    # on the person table to construct the result set.\n    query = (Note\n             .select(Note.content, Note.timestamp, Person.first, Person.last)\n             .join(max_note, on=((max_note.c.person_id == Note.person_id) &\n                                 (max_note.c.max_ts == Note.timestamp)))\n             .join(Person, on=(Note.person_id == Person.id))\n             .order_by(Person.first, Person.last))\n\n    for row in query.namedtuples():\n        print(row.first, row.last, ':', row.timestamp, '-', row.content)\n\nIn the join predicate for the join on the *max_note* subquery, we can reference\ncolumns in the subquery using the magical \".c\" attribute. So,\n*max_note.c.max_ts* is translated into \"the max_ts column value from the\nmax_note subquery\".\n\nWe can also use the \".c\" magic attribute to access columns on tables that do\nnot explicitly define their columns, like we did with the Reminder table.\nHere's a simple query to get all reminders for today, along with their\nassociated note content:\n\n.. code-block:: python\n\n    today = datetime.date.today()\n    tomorrow = today + datetime.timedelta(days=1)\n\n    query = (Reminder\n             .select(Reminder.c.alarm, Note.content)\n             .join(Note, on=(Reminder.c.note_id == Note.id))\n             .where(Reminder.c.alarm.between(today, tomorrow))\n             .order_by(Reminder.c.alarm))\n    for row in query:\n        print(row['alarm'], row['content'])\n\n.. note::\n    The \".c\" attribute will not work on tables that explicitly define their\n    columns, to prevent confusion.\n\nInsert Queries\n--------------\n\nInserting data is straightforward. We can specify data to\n:meth:`~Table.insert` in two different ways (in both cases, the ID of the\nnew row is returned):\n\n.. code-block:: python\n\n    # Using keyword arguments:\n    zaizee_id = Person.insert(first='zaizee', last='cat').execute()\n\n    # Using column: value mappings:\n    Note.insert({\n        Note.person_id: zaizee_id,\n        Note.content: 'meeeeowwww',\n        Note.timestamp: datetime.datetime.now()}).execute()\n\nIt is easy to bulk-insert data, just pass in either:\n\n* A list of dictionaries (all must have the same keys/columns).\n* A list of tuples, if the columns are specified explicitly.\n\nExamples:\n\n.. code-block:: python\n\n    people = [\n        {'first': 'Bob', 'last': 'Foo'},\n        {'first': 'Herb', 'last': 'Bar'},\n        {'first': 'Nuggie', 'last': 'Bar'}]\n\n    # Inserting multiple rows returns the ID of the last-inserted row.\n    last_id = Person.insert(people).execute()\n\n    # We can also specify row tuples, so long as we tell Peewee which\n    # columns the tuple values correspond to:\n    people = [\n        ('Bob', 'Foo'),\n        ('Herb', 'Bar'),\n        ('Nuggie', 'Bar')]\n    Person.insert(people, columns=[Person.first, Person.last]).execute()\n\nUpdate Queries\n--------------\n\n:meth:`~Table.update` queries accept either keyword arguments or a\ndictionary mapping column to value, just like :meth:`~Table.insert`.\n\nExamples:\n\n.. code-block:: python\n\n    # \"Bob\" changed his last name from \"Foo\" to \"Baze\".\n    nrows = (Person\n             .update(last='Baze')\n             .where((Person.first == 'Bob') &\n                    (Person.last == 'Foo'))\n             .execute())\n\n    # Use dictionary mapping column to value.\n    nrows = (Person\n             .update({Person.last: 'Baze'})\n             .where((Person.first == 'Bob') &\n                    (Person.last == 'Foo'))\n             .execute())\n\nYou can also use expressions as the value to perform an atomic update. Imagine\nwe have a *PageView* table and we need to atomically increment the page-view\ncount for some URL:\n\n.. code-block:: python\n\n    # Do an atomic update:\n    (PageView\n     .update({PageView.count: PageView.count + 1})\n     .where(PageView.url == some_url)\n     .execute())\n\nDelete Queries\n--------------\n\n:meth:`~Table.delete` queries are simplest of all, as they do not accept any\narguments:\n\n.. code-block:: python\n\n    # Delete all notes created before 2018, returning number deleted.\n    n = Note.delete().where(Note.timestamp < datetime.date(2018, 1, 1)).execute()\n\nBecause DELETE (and UPDATE) queries do not support joins, we can use subqueries\nto delete rows based on values in related tables. For example, here is how you\nwould delete all notes by anyone whose last name is \"Foo\":\n\n.. code-block:: python\n\n    # Get the id of all people whose last name is \"Foo\".\n    foo_people = Person.select(Person.id).where(Person.last == 'Foo')\n\n    # Delete all notes by any person whose ID is in the previous query.\n    Note.delete().where(Note.person_id.in_(foo_people)).execute()\n\nQuery Objects\n-------------\n\nOne of the fundamental limitations of the abstractions provided by Peewee 2.x\nwas the absence of a class that represented a structured query with no relation\nto a given model class.\n\nAn example of this might be computing aggregate values over a subquery. For\nexample, the :meth:`~SelectBase.count` method, which returns the count of\nrows in an arbitrary query, is implemented by wrapping the query:\n\n.. code-block:: sql\n\n    SELECT COUNT(1) FROM (...)\n\nTo accomplish this with Peewee, the implementation is written in this way:\n\n.. code-block:: python\n\n    def count(query):\n        # Select([source1, ... sourcen], [column1, ...columnn])\n        wrapped = Select(from_list=[query], columns=[fn.COUNT(SQL('1'))])\n        curs = wrapped.tuples().execute(db)\n        return curs[0][0]  # Return first column from first row of result.\n\nWe can actually express this more concisely using the\n:meth:`~SelectBase.scalar` method, which is suitable for returning values\nfrom aggregate queries:\n\n.. code-block:: python\n\n    def count(query):\n        wrapped = Select(from_list=[query], columns=[fn.COUNT(SQL('1'))])\n        return wrapped.scalar(db)\n\nThe :ref:`query-library` document has a more complex example, in which we\nwrite a query for a facility with the highest number of available slots booked:\n\nThe SQL we wish to express is:\n\n.. code-block:: sql\n\n    SELECT facid, total FROM (\n      SELECT facid, SUM(slots) AS total,\n             rank() OVER (order by SUM(slots) DESC) AS rank\n      FROM bookings\n      GROUP BY facid\n    ) AS ranked\n    WHERE rank = 1\n\nWe can express this fairly elegantly by using a plain :class:`Select` for\nthe outer query:\n\n.. code-block:: python\n\n    # Store rank expression in variable for readability.\n    rank_expr = fn.rank().over(order_by=[fn.SUM(Booking.slots).desc()])\n\n    subq = (Booking\n            .select(Booking.facility, fn.SUM(Booking.slots).alias('total'),\n                    rank_expr.alias('rank'))\n            .group_by(Booking.facility))\n\n    # Use a plain \"Select\" to create outer query.\n    query = (Select(columns=[subq.c.facid, subq.c.total])\n             .from_(subq)\n             .where(subq.c.rank == 1)\n             .tuples())\n\n    # Iterate over the resulting facility ID(s) and total(s):\n    for facid, total in query.execute(db):\n        print(facid, total)\n\nFor another example, let's create a recursive common table expression to\ncalculate the first 10 fibonacci numbers:\n\n.. code-block:: python\n\n    base = Select(columns=(\n        Value(1).alias('n'),\n        Value(0).alias('fib_n'),\n        Value(1).alias('next_fib_n'))).cte('fibonacci', recursive=True)\n\n    n = (base.c.n + 1).alias('n')\n    recursive_term = Select(columns=(\n        n,\n        base.c.next_fib_n,\n        base.c.fib_n + base.c.next_fib_n)).from_(base).where(n < 10)\n\n    fibonacci = base.union_all(recursive_term)\n    query = fibonacci.select_from(fibonacci.c.n, fibonacci.c.fib_n)\n\n    results = list(query.execute(db))\n\n    # Generates the following result list:\n    [{'fib_n': 0, 'n': 1},\n     {'fib_n': 1, 'n': 2},\n     {'fib_n': 1, 'n': 3},\n     {'fib_n': 2, 'n': 4},\n     {'fib_n': 3, 'n': 5},\n     {'fib_n': 5, 'n': 6},\n     {'fib_n': 8, 'n': 7},\n     {'fib_n': 13, 'n': 8},\n     {'fib_n': 21, 'n': 9},\n     {'fib_n': 34, 'n': 10}]\n\nMore\n----\n\nFor a description of the various classes used to describe a SQL AST, see the\n:ref:`query builder API documentation <query-builder-api>`.\n\nIf you're interested in learning more, you can also check out the `project\nsource code <https://github.com/coleifer/peewee>`_.\n"
  },
  {
    "path": "docs/peewee/query_library.rst",
    "content": ".. _query-library:\n\nQuery Examples Library\n======================\n\nThese query examples are taken from the site `Postgresql Exercises\n<https://pgexercises.com/>`_. A sample data-set can be found on the `getting\nstarted page <https://pgexercises.com/gettingstarted.html>`__.\n\nDirect download: `clubdata.sql <https://raw.githubusercontent.com/coleifer/peewee/refs/heads/master/docs/clubdata.sql>`__\n\n.. contents:: On this page\n   :local:\n   :depth: 1\n\nModel Definitions\n-----------------\n\nHere is a visual representation of the schema used in these examples:\n\n.. image:: schema-horizontal.png\n\nTo begin working with the data, we'll define the model classes that correspond\nto the tables in the diagram.\n\n.. note::\n    In some cases we explicitly specify column names for a particular field.\n    This is so our models are compatible with the database schema used for the\n    postgres exercises.\n\n.. code-block:: python\n\n    from functools import partial\n    from peewee import *\n\n\n    db = PostgresqlDatabase('peewee_test')\n\n    class BaseModel(Model):\n        class Meta:\n            database = db\n\n    class Member(BaseModel):\n        memid = AutoField()  # Auto-incrementing primary key.\n        surname = CharField()\n        firstname = CharField()\n        address = CharField(max_length=300)\n        zipcode = IntegerField()\n        telephone = CharField()\n        recommendedby = ForeignKeyField('self', backref='recommended',\n                                        column_name='recommendedby', null=True)\n        joindate = DateTimeField()\n\n        class Meta:\n            table_name = 'members'\n\n\n    # Conveniently declare decimal fields suitable for storing currency.\n    MoneyField = partial(DecimalField, decimal_places=2)\n\n\n    class Facility(BaseModel):\n        facid = AutoField()\n        name = CharField()\n        membercost = MoneyField()\n        guestcost = MoneyField()\n        initialoutlay = MoneyField()\n        monthlymaintenance = MoneyField()\n\n        class Meta:\n            table_name = 'facilities'\n\n\n    class Booking(BaseModel):\n        bookid = AutoField()\n        facility = ForeignKeyField(Facility, column_name='facid')\n        member = ForeignKeyField(Member, column_name='memid')\n        starttime = DateTimeField()\n        slots = IntegerField()\n\n        class Meta:\n            table_name = 'bookings'\n\n\nSchema Creation\n---------------\n\nIf you downloaded the SQL file from the Postgresql Exercises site, then you can\nload the data into a Postgresql database using the following commands::\n\n    createdb peewee_test\n    psql -U postgres -f clubdata.sql -d peewee_test -x -q\n\nTo create the schema using Peewee, without loading the sample data, you can run\nthe following:\n\n.. code-block:: python\n\n    # Assumes you have created the database \"peewee_test\" already.\n    db.create_tables([Member, Facility, Booking])\n\n\nBasic Exercises\n---------------\n\nThis category deals with the basics of SQL. It covers select and where clauses,\ncase expressions, unions, and a few other odds and ends.\n\nRetrieve everything\n^^^^^^^^^^^^^^^^^^^\n\nRetrieve all information from facilities table.\n\n.. code-block:: sql\n\n    SELECT * FROM facilities\n\n.. code-block:: python\n\n    # By default, when no fields are explicitly passed to select(), all fields\n    # will be selected.\n    query = Facility.select()\n\nRetrieve specific columns from a table\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nRetrieve names of facilities and cost to members.\n\n.. code-block:: sql\n\n    SELECT name, membercost FROM facilities;\n\n.. code-block:: python\n\n    query = Facility.select(Facility.name, Facility.membercost)\n\n    # To iterate:\n    for facility in query:\n        print(facility.name)\n\nControl which rows are retrieved\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nRetrieve list of facilities that have a cost to members.\n\n.. code-block:: sql\n\n    SELECT * FROM facilities WHERE membercost > 0\n\n.. code-block:: python\n\n    query = Facility.select().where(Facility.membercost > 0)\n\nControl which rows are retrieved - part 2\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nRetrieve list of facilities that have a cost to members, and that fee is less\nthan 1/50th of the monthly maintenance cost. Return id, name, cost and\nmonthly-maintenance.\n\n.. code-block:: sql\n\n    SELECT facid, name, membercost, monthlymaintenance\n    FROM facilities\n    WHERE membercost > 0 AND membercost < (monthlymaintenance / 50)\n\n.. code-block:: python\n\n    query = (Facility\n             .select(Facility.facid, Facility.name, Facility.membercost,\n                     Facility.monthlymaintenance)\n             .where(\n                 (Facility.membercost > 0) &\n                 (Facility.membercost < (Facility.monthlymaintenance / 50))))\n\nBasic string searches\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce a list of all facilities with the word 'Tennis' in their\nname?\n\n.. code-block:: sql\n\n    SELECT * FROM facilities WHERE name ILIKE '%tennis%';\n\n.. code-block:: python\n\n    query = Facility.select().where(Facility.name.contains('tennis'))\n\n    # OR use the exponent operator. Note: you must include wildcards here:\n    query = Facility.select().where(Facility.name ** '%tennis%')\n\n\nMatching against multiple possible values\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you retrieve the details of facilities with ID 1 and 5? Try to do it\nwithout using the OR operator.\n\n.. code-block:: sql\n\n    SELECT * FROM facilities WHERE facid IN (1, 5);\n\n.. code-block:: python\n\n    query = Facility.select().where(Facility.facid.in_([1, 5]))\n\n    # OR:\n    query = Facility.select().where((Facility.facid == 1) |\n                                    (Facility.facid == 5))\n\nClassify results into buckets\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce a list of facilities, with each labelled as 'cheap' or\n'expensive' depending on if their monthly maintenance cost is more than $100?\nReturn the name and monthly maintenance of the facilities in question.\n\n.. code-block:: sql\n\n    SELECT name,\n    CASE WHEN monthlymaintenance > 100 THEN 'expensive' ELSE 'cheap' END\n    FROM facilities;\n\n.. code-block:: python\n\n    cost = Case(None, [(Facility.monthlymaintenance > 100, 'expensive')], 'cheap')\n    query = Facility.select(Facility.name, cost.alias('cost'))\n\n.. note:: See documentation :class:`Case` for more examples.\n\n\nWorking with dates\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce a list of members who joined after the start of September\n2012? Return the memid, surname, firstname, and joindate of the members in\nquestion.\n\n.. code-block:: sql\n\n    SELECT memid, surname, firstname, joindate FROM members\n    WHERE joindate >= '2012-09-01';\n\n.. code-block:: python\n\n    query = (Member\n             .select(Member.memid, Member.surname, Member.firstname, Member.joindate)\n             .where(Member.joindate >= datetime.date(2012, 9, 1)))\n\n\nRemoving duplicates, and ordering results\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce an ordered list of the first 10 surnames in the members\ntable? The list must not contain duplicates.\n\n.. code-block:: sql\n\n    SELECT DISTINCT surname FROM members ORDER BY surname LIMIT 10;\n\n.. code-block:: python\n\n    query = (Member\n             .select(Member.surname)\n             .order_by(Member.surname)\n             .limit(10)\n             .distinct())\n\n\nCombining results from multiple queries\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou, for some reason, want a combined list of all surnames and all facility\nnames.\n\n.. code-block:: sql\n\n    SELECT surname FROM members UNION SELECT name FROM facilities;\n\n.. code-block:: python\n\n    lhs = Member.select(Member.surname)\n    rhs = Facility.select(Facility.name)\n    query = lhs | rhs\n\nQueries can be composed using the following operators:\n\n* ``|`` - ``UNION``\n* ``+`` - ``UNION ALL``\n* ``&`` - ``INTERSECT``\n* ``-`` - ``EXCEPT``\n\nSimple aggregation\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou'd like to get the signup date of your last member. How can you retrieve\nthis information?\n\n.. code-block:: sql\n\n    SELECT MAX(join_date) FROM members;\n\n.. code-block:: python\n\n    query = Member.select(fn.MAX(Member.joindate))\n    # To conveniently obtain a single scalar value, use \"scalar()\":\n    # max_join_date = query.scalar()\n\nMore aggregation\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou'd like to get the first and last name of the last member(s) who signed up\n- not just the date.\n\n.. code-block:: sql\n\n    SELECT firstname, surname, joindate FROM members\n    WHERE joindate = (SELECT MAX(joindate) FROM members);\n\n.. code-block:: python\n\n    # Use \"alias()\" to reference the same table multiple times in a query.\n    MemberAlias = Member.alias()\n    subq = MemberAlias.select(fn.MAX(MemberAlias.joindate))\n    query = (Member\n             .select(Member.firstname, Member.surname, Member.joindate)\n             .where(Member.joindate == subq))\n\n\nJoins and Subqueries\n--------------------\n\nThis category deals primarily with a foundational concept in relational\ndatabase systems: joining. Joining allows you to combine related information\nfrom multiple tables to answer a question. This isn't just beneficial for ease\nof querying: a lack of join capability encourages denormalisation of data,\nwhich increases the complexity of keeping your data internally consistent.\n\nThis topic covers inner, outer, and self joins, as well as spending a little\ntime on subqueries (queries within queries).\n\nRetrieve the start times of members' bookings\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce a list of the start times for bookings by members named\n'David Farrell'?\n\n.. code-block:: sql\n\n    SELECT starttime FROM bookings\n    INNER JOIN members ON (bookings.memid = members.memid)\n    WHERE surname = 'Farrell' AND firstname = 'David';\n\n.. code-block:: python\n\n    query = (Booking\n             .select(Booking.starttime)\n             .join(Member)\n             .where((Member.surname == 'Farrell') &\n                    (Member.firstname == 'David')))\n\n\nWork out the start times of bookings for tennis courts\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce a list of the start times for bookings for tennis courts,\nfor the date '2012-09-21'? Return a list of start time and facility name\npairings, ordered by the time.\n\n.. code-block:: sql\n\n    SELECT starttime, name\n    FROM bookings\n    INNER JOIN facilities ON (bookings.facid = facilities.facid)\n    WHERE date_trunc('day', starttime) = '2012-09-21':: date\n      AND name ILIKE 'tennis%'\n    ORDER BY starttime, name;\n\n.. code-block:: python\n\n    query = (Booking\n             .select(Booking.starttime, Facility.name)\n             .join(Facility)\n             .where(\n                 (fn.date_trunc('day', Booking.starttime) == datetime.date(2012, 9, 21)) &\n                 Facility.name.startswith('Tennis'))\n             .order_by(Booking.starttime, Facility.name))\n\n    # To retrieve the joined facility's name when iterating:\n    for booking in query:\n        print(booking.starttime, booking.facility.name)\n\n\nProduce a list of all members who have recommended another member\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you output a list of all members who have recommended another member?\nEnsure that there are no duplicates in the list, and that results are ordered\nby (surname, firstname).\n\n.. code-block:: sql\n\n    SELECT DISTINCT m.firstname, m.surname\n    FROM members AS m2\n    INNER JOIN members AS m ON (m.memid = m2.recommendedby)\n    ORDER BY m.surname, m.firstname;\n\n.. code-block:: python\n\n    MA = Member.alias()\n    query = (Member\n             .select(Member.firstname, Member.surname)\n             .join(MA, on=(MA.recommendedby == Member.memid))\n             .order_by(Member.surname, Member.firstname))\n\n\nProduce a list of all members, along with their recommender\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you output a list of all members, including the individual who\nrecommended them (if any)? Ensure that results are ordered by (surname,\nfirstname).\n\n.. code-block:: sql\n\n    SELECT m.firstname, m.surname, r.firstname, r.surname\n    FROM members AS m\n    LEFT OUTER JOIN members AS r ON (m.recommendedby = r.memid)\n    ORDER BY m.surname, m.firstname\n\n.. code-block:: python\n\n    MA = Member.alias()\n    query = (Member\n             .select(Member.firstname, Member.surname, MA.firstname, MA.surname)\n             .join(MA, JOIN.LEFT_OUTER, on=(Member.recommendedby == MA.memid))\n             .order_by(Member.surname, Member.firstname))\n\n    # To display the recommender's name when iterating:\n    for m in query:\n        print(m.firstname, m.surname)\n        if m.recommendedby:\n            print('  ', m.recommendedby.firstname, m.recommendedby.surname)\n\n\nProduce a list of all members who have used a tennis court\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce a list of all members who have used a tennis court?\nInclude in your output the name of the court, and the name of the member\nformatted as a single column. Ensure no duplicate data, and order by the\nmember name.\n\n.. code-block:: sql\n\n    SELECT DISTINCT m.firstname || ' ' || m.surname AS member, f.name AS facility\n    FROM members AS m\n    INNER JOIN bookings AS b ON (m.memid = b.memid)\n    INNER JOIN facilities AS f ON (b.facid = f.facid)\n    WHERE f.name LIKE 'Tennis%'\n    ORDER BY member, facility;\n\n.. code-block:: python\n\n    fullname = Member.firstname + ' ' + Member.surname\n    query = (Member\n             .select(fullname.alias('member'), Facility.name.alias('facility'))\n             .join(Booking)\n             .join(Facility)\n             .where(Facility.name.startswith('Tennis'))\n             .order_by(fullname, Facility.name)\n             .distinct())\n\n\nProduce a list of costly bookings\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you produce a list of bookings on the day of 2012-09-14 which will\ncost the member (or guest) more than $30? Remember that guests have different\ncosts to members (the listed costs are per half-hour 'slot'), and the guest\nuser is always ID 0. Include in your output the name of the facility, the\nname of the member formatted as a single column, and the cost. Order by\ndescending cost, and do not use any subqueries.\n\n.. code-block:: sql\n\n    SELECT m.firstname || ' ' || m.surname AS member,\n           f.name AS facility,\n           (CASE WHEN m.memid = 0 THEN f.guestcost * b.slots\n            ELSE f.membercost * b.slots END) AS cost\n    FROM members AS m\n    INNER JOIN bookings AS b ON (m.memid = b.memid)\n    INNER JOIN facilities AS f ON (b.facid = f.facid)\n    WHERE (date_trunc('day', b.starttime) = '2012-09-14') AND\n     ((m.memid = 0 AND b.slots * f.guestcost > 30) OR\n      (m.memid > 0 AND b.slots * f.membercost > 30))\n    ORDER BY cost DESC;\n\n.. code-block:: python\n\n    cost = Case(Member.memid, (\n        (0, Booking.slots * Facility.guestcost),\n    ), (Booking.slots * Facility.membercost))\n    fullname = Member.firstname + ' ' + Member.surname\n\n    query = (Member\n             .select(fullname.alias('member'), Facility.name.alias('facility'),\n                     cost.alias('cost'))\n             .join(Booking)\n             .join(Facility)\n             .where(\n                 (fn.date_trunc('day', Booking.starttime) == datetime.date(2012, 9, 14)) &\n                 (cost > 30))\n             .order_by(SQL('cost').desc()))\n\n    # To iterate over the results, it might be easiest to use namedtuples:\n    for row in query.namedtuples():\n        print(row.member, row.facility, row.cost)\n\n\nProduce a list of all members, along with their recommender, using no joins.\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can you output a list of all members, including the individual who\nrecommended them (if any), without using any joins? Ensure that there are no\nduplicates in the list, and that each firstname + surname pairing is\nformatted as a column and ordered.\n\n.. code-block:: sql\n\n    SELECT DISTINCT m.firstname || ' ' || m.surname AS member,\n       (SELECT r.firstname || ' ' || r.surname\n        FROM members AS r\n        WHERE m.recommendedby = r.memid) AS recommended\n    FROM members AS m ORDER BY member;\n\n.. code-block:: python\n\n    MA = Member.alias()\n    subq = (MA\n            .select(MA.firstname + ' ' + MA.surname)\n            .where(Member.recommendedby == MA.memid))\n    query = (Member\n             .select(fullname.alias('member'), subq.alias('recommended'))\n             .order_by(fullname))\n\n\nProduce a list of costly bookings, using a subquery\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe \"Produce a list of costly bookings\" exercise contained some messy logic: we\nhad to calculate the booking cost in both the WHERE clause and the CASE\nstatement. Try to simplify this calculation using subqueries.\n\n.. code-block:: sql\n\n    SELECT member, facility, cost from (\n      SELECT\n      m.firstname || ' ' || m.surname as member,\n      f.name as facility,\n      CASE WHEN m.memid = 0 THEN b.slots * f.guestcost\n      ELSE b.slots * f.membercost END AS cost\n      FROM members AS m\n      INNER JOIN bookings AS b ON m.memid = b.memid\n      INNER JOIN facilities AS f ON b.facid = f.facid\n      WHERE date_trunc('day', b.starttime) = '2012-09-14'\n    ) as bookings\n    WHERE cost > 30\n    ORDER BY cost DESC;\n\n.. code-block:: python\n\n    cost = Case(Member.memid, (\n        (0, Booking.slots * Facility.guestcost),\n    ), (Booking.slots * Facility.membercost))\n\n    iq = (Member\n          .select(fullname.alias('member'), Facility.name.alias('facility'),\n                  cost.alias('cost'))\n          .join(Booking)\n          .join(Facility)\n          .where(fn.date_trunc('day', Booking.starttime) == datetime.date(2012, 9, 14)))\n\n    query = (Member\n             .select(iq.c.member, iq.c.facility, iq.c.cost)\n             .from_(iq)\n             .where(iq.c.cost > 30)\n             .order_by(SQL('cost').desc()))\n\n    # To iterate, try using dicts:\n    for row in query.dicts():\n        print(row['member'], row['facility'], row['cost'])\n\n\nModifying Data\n--------------\n\nQuerying data is all well and good, but at some point you're probably going to\nwant to put data into your database! This section deals with inserting,\nupdating, and deleting information. Operations that alter your data like this\nare collectively known as Data Manipulation Language, or DML.\n\nIn previous sections, we returned to you the results of the query you've\nperformed. Since modifications like the ones we're making in this section don't\nreturn any query results, we instead show you the updated content of the table\nyou're supposed to be working on.\n\nInsert some data into a table\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe club is adding a new facility - a spa. We need to add it into the\nfacilities table. Use the following values: facid: 9, Name: 'Spa',\nmembercost: 20, guestcost: 30, initialoutlay: 100000, monthlymaintenance: 800\n\n.. code-block:: sql\n\n    INSERT INTO \"facilities\" (\"facid\", \"name\", \"membercost\", \"guestcost\",\n    \"initialoutlay\", \"monthlymaintenance\") VALUES (9, 'Spa', 20, 30, 100000, 800)\n\n.. code-block:: python\n\n    res = Facility.insert({\n        Facility.facid: 9,\n        Facility.name: 'Spa',\n        Facility.membercost: 20,\n        Facility.guestcost: 30,\n        Facility.initialoutlay: 100000,\n        Facility.monthlymaintenance: 800}).execute()\n\n    # OR:\n    res = (Facility\n           .insert(facid=9, name='Spa', membercost=20, guestcost=30,\n                   initialoutlay=100000, monthlymaintenance=800)\n           .execute())\n\n\nInsert multiple rows of data into a table\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn the previous exercise, you learned how to add a facility. Now you're going\nto add multiple facilities in one command. Use the following values:\n\nfacid: 9, Name: 'Spa', membercost: 20, guestcost: 30, initialoutlay: 100000,\nmonthlymaintenance: 800.\n\nfacid: 10, Name: 'Squash Court 2', membercost: 3.5, guestcost: 17.5,\ninitialoutlay: 5000, monthlymaintenance: 80.\n\n.. code-block:: sql\n\n    INSERT INTO \"facilities\" (...)\n    VALUES (9, ...), (10, ...);\n\n.. code-block:: python\n\n    data = [\n        {'facid': 9, 'name': 'Spa', 'membercost': 20, 'guestcost': 30,\n         'initialoutlay': 100000, 'monthlymaintenance': 800},\n        {'facid': 10, 'name': 'Squash Court 2', 'membercost': 3.5,\n         'guestcost': 17.5, 'initialoutlay': 5000, 'monthlymaintenance': 80}]\n    res = Facility.insert_many(data).execute()\n\n\nInsert calculated data into a table\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nLet's try adding the spa to the facilities table again. This time, though, we\nwant to automatically generate the value for the next facid, rather than\nspecifying it as a constant. Use the following values for everything else:\nName: 'Spa', membercost: 20, guestcost: 30, initialoutlay: 100000,\nmonthlymaintenance: 800.\n\n.. code-block:: sql\n\n    INSERT INTO \"facilities\" (\"facid\", \"name\", \"membercost\", \"guestcost\",\n      \"initialoutlay\", \"monthlymaintenance\")\n    SELECT (SELECT (MAX(\"facid\") + 1) FROM \"facilities\") AS _,\n            'Spa', 20, 30, 100000, 800;\n\n.. code-block:: python\n\n    maxq = Facility.select(fn.MAX(Facility.facid) + 1)\n    subq = Select(columns=(maxq, 'Spa', 20, 30, 100000, 800))\n    res = Facility.insert_from(subq, Facility._meta.sorted_fields).execute()\n\nUpdate some existing data\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWe made a mistake when entering the data for the second tennis court. The\ninitial outlay was 10000 rather than 8000: you need to alter the data to fix\nthe error.\n\n.. code-block:: sql\n\n    UPDATE facilities SET initialoutlay = 10000 WHERE name = 'Tennis Court 2';\n\n.. code-block:: python\n\n    res = (Facility\n           .update({Facility.initialoutlay: 10000})\n           .where(Facility.name == 'Tennis Court 2')\n           .execute())\n\n    # OR:\n    res = (Facility\n           .update(initialoutlay=10000)\n           .where(Facility.name == 'Tennis Court 2')\n           .execute())\n\nUpdate multiple rows and columns at the same time\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWe want to increase the price of the tennis courts for both members and\nguests. Update the costs to be 6 for members, and 30 for guests.\n\n.. code-block:: sql\n\n    UPDATE facilities SET membercost=6, guestcost=30 WHERE name ILIKE 'Tennis%';\n\n.. code-block:: python\n\n    nrows = (Facility\n             .update(membercost=6, guestcost=30)\n             .where(Facility.name.startswith('Tennis'))\n             .execute())\n\nUpdate a row based on the contents of another row\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWe want to alter the price of the second tennis court so that it costs 10%\nmore than the first one. Try to do this without using constant values for the\nprices, so that we can reuse the statement if we want to.\n\n.. code-block:: sql\n\n    UPDATE facilities SET\n    membercost = (SELECT membercost * 1.1 FROM facilities WHERE facid = 0),\n    guestcost = (SELECT guestcost * 1.1 FROM facilities WHERE facid = 0)\n    WHERE facid = 1;\n\n    -- OR --\n    WITH new_prices (nmc, ngc) AS (\n      SELECT membercost * 1.1, guestcost * 1.1\n      FROM facilities WHERE name = 'Tennis Court 1')\n    UPDATE facilities\n    SET membercost = new_prices.nmc, guestcost = new_prices.ngc\n    FROM new_prices\n    WHERE name = 'Tennis Court 2'\n\n.. code-block:: python\n\n    sq1 = Facility.select(Facility.membercost * 1.1).where(Facility.facid == 0)\n    sq2 = Facility.select(Facility.guestcost * 1.1).where(Facility.facid == 0)\n\n    res = (Facility\n           .update(membercost=sq1, guestcost=sq2)\n           .where(Facility.facid == 1)\n           .execute())\n\n    # OR:\n    cte = (Facility\n           .select(Facility.membercost * 1.1, Facility.guestcost * 1.1)\n           .where(Facility.name == 'Tennis Court 1')\n           .cte('new_prices', columns=('nmc', 'ngc')))\n    res = (Facility\n           .update(membercost=SQL('new_prices.nmc'), guestcost=SQL('new_prices.ngc'))\n           .with_cte(cte)\n           .from_(cte)\n           .where(Facility.name == 'Tennis Court 2')\n           .execute())\n\nDelete all bookings\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAs part of a clearout of our database, we want to delete all bookings from\nthe bookings table.\n\n.. code-block:: sql\n\n    DELETE FROM bookings;\n\n.. code-block:: python\n\n    nrows = Booking.delete().execute()\n\n\nDelete a member from the members table\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWe want to remove member 37, who has never made a booking, from our database.\n\n.. code-block:: sql\n\n    DELETE FROM members WHERE memid = 37;\n\n.. code-block:: python\n\n    nrows = Member.delete().where(Member.memid == 37).execute()\n\nDelete based on a subquery\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nHow can we make that more general, to delete all members who have never made\na booking?\n\n.. code-block:: sql\n\n    DELETE FROM members WHERE NOT EXISTS (\n      SELECT * FROM bookings WHERE bookings.memid = members.memid);\n\n.. code-block:: python\n\n    subq = Booking.select().where(Booking.member == Member.memid)\n    nrows = Member.delete().where(~fn.EXISTS(subq)).execute()\n\n\nAggregation\n-----------\n\nAggregation is one of those capabilities that really make you appreciate the\npower of relational database systems. It allows you to move beyond merely\npersisting your data, into the realm of asking truly interesting questions that\ncan be used to inform decision making. This category covers aggregation at\nlength, making use of standard grouping as well as more recent window\nfunctions.\n\nCount the number of facilities\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFor our first foray into aggregates, we're going to stick to something\nsimple. We want to know how many facilities exist - simply produce a total\ncount.\n\n.. code-block:: sql\n\n    SELECT COUNT(facid) FROM facilities;\n\n.. code-block:: python\n\n    query = Facility.select(fn.COUNT(Facility.facid))\n    count = query.scalar()\n\n    # OR:\n    count = Facility.select().count()\n\nCount the number of expensive facilities\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a count of the number of facilities that have a cost to guests of 10\nor more.\n\n.. code-block:: sql\n\n    SELECT COUNT(facid) FROM facilities WHERE guestcost >= 10\n\n.. code-block:: python\n\n    query = Facility.select(fn.COUNT(Facility.facid)).where(Facility.guestcost >= 10)\n    count = query.scalar()\n\n    # OR:\n    # count = Facility.select().where(Facility.guestcost >= 10).count()\n\nCount the number of recommendations each member makes.\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a count of the number of recommendations each member has made. Order\nby member ID.\n\n.. code-block:: sql\n\n    SELECT recommendedby, COUNT(memid) FROM members\n    WHERE recommendedby IS NOT NULL\n    GROUP BY recommendedby\n    ORDER BY recommendedby\n\n.. code-block:: python\n\n    query = (Member\n             .select(Member.recommendedby, fn.COUNT(Member.memid))\n             .where(Member.recommendedby.is_null(False))\n             .group_by(Member.recommendedby)\n             .order_by(Member.recommendedby))\n\n\nList the total slots booked per facility\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of the total number of slots booked per facility. For now,\njust produce an output table consisting of facility id and slots, sorted by\nfacility id.\n\n.. code-block:: sql\n\n    SELECT facid, SUM(slots) FROM bookings GROUP BY facid ORDER BY facid;\n\n.. code-block:: python\n\n    query = (Booking\n             .select(Booking.facid, fn.SUM(Booking.slots))\n             .group_by(Booking.facid)\n             .order_by(Booking.facid))\n\n\nList the total slots booked per facility in a given month\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of the total number of slots booked per facility in the month\nof September 2012. Produce an output table consisting of facility id and\nslots, sorted by the number of slots.\n\n.. code-block:: sql\n\n    SELECT facid, SUM(slots)\n    FROM bookings\n    WHERE (date_trunc('month', starttime) = '2012-09-01'::dates)\n    GROUP BY facid\n    ORDER BY SUM(slots)\n\n.. code-block:: python\n\n    query = (Booking\n             .select(Booking.facility, fn.SUM(Booking.slots))\n             .where(fn.date_trunc('month', Booking.starttime) == datetime.date(2012, 9, 1))\n             .group_by(Booking.facility)\n             .order_by(fn.SUM(Booking.slots)))\n\nList the total slots booked per facility per month\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of the total number of slots booked per facility per month in\nthe year of 2012. Produce an output table consisting of facility id and\nslots, sorted by the id and month.\n\n.. code-block:: sql\n\n    SELECT facid, date_part('month', starttime), SUM(slots)\n    FROM bookings\n    WHERE date_part('year', starttime) = 2012\n    GROUP BY facid, date_part('month', starttime)\n    ORDER BY facid, date_part('month', starttime)\n\n.. code-block:: python\n\n    month = fn.date_part('month', Booking.starttime)\n    query = (Booking\n             .select(Booking.facility, month, fn.SUM(Booking.slots))\n             .where(fn.date_part('year', Booking.starttime) == 2012)\n             .group_by(Booking.facility, month)\n             .order_by(Booking.facility, month))\n\n\nFind the count of members who have made at least one booking\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFind the total number of members who have made at least one booking.\n\n.. code-block:: sql\n\n    SELECT COUNT(DISTINCT memid) FROM bookings\n\n    -- OR --\n    SELECT COUNT(1) FROM (SELECT DISTINCT memid FROM bookings) AS _\n\n.. code-block:: python\n\n    query = Booking.select(fn.COUNT(Booking.member.distinct()))\n\n    # OR:\n    query = Booking.select(Booking.member).distinct()\n    count = query.count()  # count() wraps in SELECT COUNT(1) FROM (...)\n\nList facilities with more than 1000 slots booked\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of facilities with more than 1000 slots booked. Produce an\noutput table consisting of facility id and hours, sorted by facility id.\n\n.. code-block:: sql\n\n    SELECT facid, SUM(slots) FROM bookings\n    GROUP BY facid\n    HAVING SUM(slots) > 1000\n    ORDER BY facid;\n\n.. code-block:: python\n\n    query = (Booking\n             .select(Booking.facility, fn.SUM(Booking.slots))\n             .group_by(Booking.facility)\n             .having(fn.SUM(Booking.slots) > 1000)\n             .order_by(Booking.facility))\n\nFind the total revenue of each facility\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of facilities along with their total revenue. The output table\nshould consist of facility name and revenue, sorted by revenue. Remember that\nthere's a different cost for guests and members!\n\n.. code-block:: sql\n\n    SELECT f.name, SUM(b.slots * (\n    CASE WHEN b.memid = 0 THEN f.guestcost ELSE f.membercost END)) AS revenue\n    FROM bookings AS b\n    INNER JOIN facilities AS f ON b.facid = f.facid\n    GROUP BY f.name\n    ORDER BY revenue;\n\n.. code-block:: python\n\n    revenue = fn.SUM(Booking.slots * Case(None, (\n        (Booking.member == 0, Facility.guestcost),\n    ), Facility.membercost))\n\n    query = (Facility\n             .select(Facility.name, revenue.alias('revenue'))\n             .join(Booking)\n             .group_by(Facility.name)\n             .order_by(SQL('revenue')))\n\n\nFind facilities with a total revenue less than 1000\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of facilities with a total revenue less than 1000. Produce an\noutput table consisting of facility name and revenue, sorted by revenue.\nRemember that there's a different cost for guests and members!\n\n.. code-block:: sql\n\n    SELECT f.name, SUM(b.slots * (\n    CASE WHEN b.memid = 0 THEN f.guestcost ELSE f.membercost END)) AS revenue\n    FROM bookings AS b\n    INNER JOIN facilities AS f ON b.facid = f.facid\n    GROUP BY f.name\n    HAVING SUM(b.slots * ...) < 1000\n    ORDER BY revenue;\n\n.. code-block:: python\n\n    # Same definition as previous example.\n    revenue = fn.SUM(Booking.slots * Case(None, (\n        (Booking.member == 0, Facility.guestcost),\n    ), Facility.membercost))\n\n    query = (Facility\n             .select(Facility.name, revenue.alias('revenue'))\n             .join(Booking)\n             .group_by(Facility.name)\n             .having(revenue < 1000)\n             .order_by(SQL('revenue')))\n\nOutput the facility id that has the highest number of slots booked\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nOutput the facility id that has the highest number of slots booked.\n\n.. code-block:: sql\n\n    SELECT facid, SUM(slots) FROM bookings\n    GROUP BY facid\n    ORDER BY SUM(slots) DESC\n    LIMIT 1\n\n.. code-block:: python\n\n    query = (Booking\n             .select(Booking.facility, fn.SUM(Booking.slots))\n             .group_by(Booking.facility)\n             .order_by(fn.SUM(Booking.slots).desc())\n             .limit(1))\n\n    # Retrieve multiple scalar values by calling scalar() with as_tuple=True.\n    facid, nslots = query.scalar(as_tuple=True)\n\nList the total slots booked per facility per month, part 2\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of the total number of slots booked per facility per month in\nthe year of 2012. In this version, include output rows containing totals for\nall months per facility, and a total for all months for all facilities. The\noutput table should consist of facility id, month and slots, sorted by the id\nand month. When calculating the aggregated values for all months and all\nfacids, return null values in the month and facid columns.\n\nPostgres ONLY.\n\n.. code-block:: sql\n\n    SELECT facid, date_part('month', starttime), SUM(slots)\n    FROM booking\n    WHERE date_part('year', starttime) = 2012\n    GROUP BY ROLLUP(facid, date_part('month', starttime))\n    ORDER BY facid, date_part('month', starttime)\n\n.. code-block:: python\n\n    month = fn.date_part('month', Booking.starttime)\n    query = (Booking\n             .select(Booking.facility,\n                     month.alias('month'),\n                     fn.SUM(Booking.slots))\n             .where(fn.date_part('year', Booking.starttime) == 2012)\n             .group_by(fn.ROLLUP(Booking.facility, month))\n             .order_by(Booking.facility, month))\n\n\nList the total hours booked per named facility\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of the total number of hours booked per facility, remembering\nthat a slot lasts half an hour. The output table should consist of the\nfacility id, name, and hours booked, sorted by facility id.\n\n.. code-block:: sql\n\n    SELECT f.facid, f.name, SUM(b.slots) * .5\n    FROM facilities AS f\n    INNER JOIN bookings AS b ON (f.facid = b.facid)\n    GROUP BY f.facid, f.name\n    ORDER BY f.facid\n\n.. code-block:: python\n\n    query = (Facility\n             .select(Facility.facid, Facility.name, fn.SUM(Booking.slots) * .5)\n             .join(Booking)\n             .group_by(Facility.facid, Facility.name)\n             .order_by(Facility.facid))\n\n\nList each member's first booking after September 1st 2012\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of each member name, id, and their first booking after\nSeptember 1st 2012. Order by member ID.\n\n.. code-block:: sql\n\n    SELECT m.surname, m.firstname, m.memid, min(b.starttime) as starttime\n    FROM members AS m\n    INNER JOIN bookings AS b ON b.memid = m.memid\n    WHERE starttime >= '2012-09-01'\n    GROUP BY m.surname, m.firstname, m.memid\n    ORDER BY m.memid;\n\n.. code-block:: python\n\n    query = (Member\n             .select(Member.surname, Member.firstname, Member.memid,\n                     fn.MIN(Booking.starttime).alias('starttime'))\n             .join(Booking)\n             .where(Booking.starttime >= datetime.date(2012, 9, 1))\n             .group_by(Member.surname, Member.firstname, Member.memid)\n             .order_by(Member.memid))\n\nProduce a list of member names, with each row containing the total member count\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of member names, with each row containing the total member\ncount. Order by join date.\n\nPostgres ONLY (as written).\n\n.. code-block:: sql\n\n    SELECT COUNT(*) OVER(), firstname, surname\n    FROM members ORDER BY joindate\n\n.. code-block:: python\n\n    query = (Member\n             .select(fn.COUNT(Member.memid).over(), Member.firstname,\n                     Member.surname)\n             .order_by(Member.joindate))\n\nProduce a numbered list of members\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a monotonically increasing numbered list of members, ordered by their\ndate of joining. Remember that member IDs are not guaranteed to be\nsequential.\n\nPostgres ONLY (as written).\n\n.. code-block:: sql\n\n    SELECT row_number() OVER (ORDER BY joindate), firstname, surname\n    FROM members ORDER BY joindate;\n\n.. code-block:: python\n\n    query = (Member\n             .select(fn.row_number().over(order_by=[Member.joindate]),\n                     Member.firstname, Member.surname)\n             .order_by(Member.joindate))\n\nOutput the facility id that has the highest number of slots booked, again\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nOutput the facility id that has the highest number of slots booked. Ensure\nthat in the event of a tie, all tieing results get output.\n\nPostgres ONLY (as written).\n\n.. code-block:: sql\n\n    SELECT facid, total FROM (\n      SELECT facid, SUM(slots) AS total,\n             rank() OVER (order by SUM(slots) DESC) AS rank\n      FROM bookings\n      GROUP BY facid\n    ) AS ranked WHERE rank = 1\n\n.. code-block:: python\n\n    rank = fn.rank().over(order_by=[fn.SUM(Booking.slots).desc()])\n\n    subq = (Booking\n            .select(Booking.facility, fn.SUM(Booking.slots).alias('total'),\n                    rank.alias('rank'))\n            .group_by(Booking.facility))\n\n    # Here we use a plain Select() to create our query.\n    query = (Select(columns=[subq.c.facid, subq.c.total])\n             .from_(subq)\n             .where(subq.c.rank == 1)\n             .bind(db))  # We must bind() it to the database.\n\n    # To iterate over the query results:\n    for facid, total in query.tuples():\n        print(facid, total)\n\nRank members by (rounded) hours used\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of members, along with the number of hours they've booked in\nfacilities, rounded to the nearest ten hours. Rank them by this rounded\nfigure, producing output of first name, surname, rounded hours, rank. Sort by\nrank, surname, and first name.\n\nPostgres ONLY (as written).\n\n.. code-block:: sql\n\n    SELECT firstname, surname,\n    ((SUM(bks.slots)+10)/20)*10 as hours,\n    rank() over (order by ((sum(bks.slots)+10)/20)*10 desc) as rank\n    FROM members AS mems\n    INNER JOIN bookings AS bks ON mems.memid = bks.memid\n    GROUP BY mems.memid\n    ORDER BY rank, surname, firstname;\n\n.. code-block:: python\n\n    hours = ((fn.SUM(Booking.slots) + 10) / 20) * 10\n    query = (Member\n             .select(Member.firstname, Member.surname, hours.alias('hours'),\n                     fn.rank().over(order_by=[hours.desc()]).alias('rank'))\n             .join(Booking)\n             .group_by(Member.memid)\n             .order_by(SQL('rank'), Member.surname, Member.firstname))\n\n\nFind the top three revenue generating facilities\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a list of the top three revenue generating facilities (including\nties). Output facility name and rank, sorted by rank and facility name.\n\nPostgres ONLY (as written).\n\n.. code-block:: sql\n\n    SELECT name, rank FROM (\n        SELECT f.name, RANK() OVER (ORDER BY SUM(\n            CASE WHEN memid = 0 THEN slots * f.guestcost\n            ELSE slots * f.membercost END) DESC) AS rank\n        FROM bookings\n        INNER JOIN facilities AS f ON bookings.facid = f.facid\n        GROUP BY f.name) AS subq\n    WHERE rank <= 3\n    ORDER BY rank;\n\n.. code-block:: python\n\n   total_cost = fn.SUM(Case(None, (\n       (Booking.member == 0, Booking.slots * Facility.guestcost),\n   ), (Booking.slots * Facility.membercost)))\n\n   subq = (Facility\n           .select(Facility.name,\n                   fn.RANK().over(order_by=[total_cost.desc()]).alias('rank'))\n           .join(Booking)\n           .group_by(Facility.name))\n\n   query = (Select(columns=[subq.c.name, subq.c.rank])\n            .from_(subq)\n            .where(subq.c.rank <= 3)\n            .order_by(subq.c.rank)\n            .bind(db))  # Here again we used plain Select, and call bind().\n\nClassify facilities by value\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nClassify facilities into equally sized groups of high, average, and low based\non their revenue. Order by classification and facility name.\n\nPostgres ONLY (as written).\n\n.. code-block:: sql\n\n    SELECT name,\n      CASE class WHEN 1 THEN 'high' WHEN 2 THEN 'average' ELSE 'low' END\n    FROM (\n      SELECT f.name, ntile(3) OVER (ORDER BY SUM(\n        CASE WHEN memid = 0 THEN slots * f.guestcost ELSE slots * f.membercost\n        END) DESC) AS class\n      FROM bookings INNER JOIN facilities AS f ON bookings.facid = f.facid\n      GROUP BY f.name\n    ) AS subq\n    ORDER BY class, name;\n\n.. code-block:: python\n\n    cost = fn.SUM(Case(None, (\n        (Booking.member == 0, Booking.slots * Facility.guestcost),\n    ), (Booking.slots * Facility.membercost)))\n    subq = (Facility\n            .select(Facility.name,\n                    fn.NTILE(3).over(order_by=[cost.desc()]).alias('klass'))\n            .join(Booking)\n            .group_by(Facility.name))\n\n    klass_case = Case(subq.c.klass, [(1, 'high'), (2, 'average')], 'low')\n    query = (Select(columns=[subq.c.name, klass_case])\n             .from_(subq)\n             .order_by(subq.c.klass, subq.c.name)\n             .bind(db))\n\nCalculate the payback time for each facility\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nBased on the 3 complete months of data so far, calculate the amount of time\neach facility will take to repay its cost of ownership. Remember to take into\naccount ongoing monthly maintenance. Output facility name and payback time in\nmonths, order by facility name. Don't worry about differences in month\nlengths, we're only looking for a rough value here!\n\n.. code-block:: sql\n\n   SELECT f.name,\n          f.initialoutlay / ((SUM(CASE\n              WHEN b.memid = 0 THEN b.slots * f.guestcost\n              ELSE b.slots * f.membercost END) / 3) - f.monthlymaintenance)\n              AS months\n   FROM facilities AS f\n   INNER JOIN bookings AS b on (b.facid = f.facid)\n   GROUP BY f.facid\n   ORDER BY f.name\n\n.. code-block:: python\n\n   # How much money has this facility produced from its bookings?\n   revenue = fn.SUM(Case(None, (\n       (Booking.member == 0, Booking.slots * Facility.guestcost),\n   ), (Booking.slots * Facility.membercost)))\n\n   # Subtract monthly maintenance from average monthly revenue.\n   revenue_less_maintenance = (revenue / 3) - Facility.monthlymaintenance\n\n   # Determine how many months needed to pay off initial outlay.\n   payback_time = Facility.initialoutlay / revenue_less_maintenance\n\n   query = (Facility\n            .select(Facility.name,\n                    payback_time.alias('months'))\n            .join(Booking)\n            .group_by(Facility.facid)\n            .order_by(Facility.name))\n\nBut, I hear you ask, what would an automatic version of this look like? One\nthat didn't need to have a hard-coded number of months in it? That's a little\nmore complicated, and involves some date arithmetic. I've factored that out\ninto a CTE to make it a little more clear.\n\n.. code-block:: sql\n\n   with monthdata as (\n      select mincompletemonth,\n             maxcompletemonth,\n             ((extract(year from maxcompletemonth)*12) +\n              extract(month from maxcompletemonth) -\n              (extract(year from mincompletemonth)*12) -\n              extract(month from mincompletemonth)) as nummonths\n      from (\n         select\n            date_trunc('month',\n               (select max(starttime) from bookings)) as maxcompletemonth,\n            date_trunc('month',\n               (select min(starttime) from bookings)) as mincompletemonth\n      ) as subq)\n   select name,\n      initialoutlay / (monthlyrevenue - monthlymaintenance) as repaytime\n      from\n         (select f.name as name,\n            f.initialoutlay as initialoutlay,\n            f.monthlymaintenance as monthlymaintenance,\n            sum(case\n               when memid = 0 then slots * f.guestcost\n               else slots * membercost\n            end)/(select nummonths from monthdata) as monthlyrevenue\n\n            from bookings as b\n            inner join facilities as f\n               on b.facid = f.facid\n            where b.starttime < (select maxcompletemonth from monthdata)\n            group by f.facid\n         ) as subq\n   order by name;\n\n.. code-block:: python\n\n   # First calculate the min and max ranges of bookings.\n   BA = Booking.alias()\n   bounds = BA.select(\n       fn.date_trunc('month', fn.MIN(BA.starttime)).alias('minmonth'),\n       fn.date_trunc('month', fn.MAX(BA.starttime)).alias('maxmonth')\n   ).alias('bounds')\n\n   # Calculate how many months the range of bookings covers.\n   extract = db.extract_date  # Helper for generating EXTRACT .. FROM.\n   q = bounds.select_from(\n       bounds.c.minmonth,\n       bounds.c.maxmonth,\n       ((extract('year', bounds.c.maxmonth) * 12) +\n        extract('month', bounds.c.maxmonth) -\n        (extract('year', bounds.c.minmonth) * 12) -\n        extract('month', bounds.c.minmonth)).alias('nmonths'))\n\n   # Indicate that we will be using this as a CTE.\n   monthdata = q.cte('monthdata')\n\n   # Subqueries to retrieve total & max month data from the CTE.\n   nmonths = Select((monthdata,), (monthdata.c.nmonths,))\n   maxmonth = Select((monthdata,), (monthdata.c.maxmonth,))\n\n   # Our familiar revenue calculation.\n   revenue = fn.SUM(Case(None, (\n       (Booking.member == 0, Booking.slots * Facility.guestcost),\n   ), (Booking.slots * Facility.membercost)))\n   revenue_less_maintenance = (revenue / nmonths) - Facility.monthlymaintenance\n\n   payback_time = Facility.initialoutlay / revenue_less_maintenance\n\n   q = (Facility\n        .select(\n            Facility.name,\n            payback_time.alias('payback_time'))\n        .join(Booking)\n        .where(Booking.starttime < maxmonth)\n        .group_by(Facility.facid)\n        .order_by(Facility.name)\n        .with_cte(monthdata))\n\nDates and Times\n---------------\n\nWork out the end time of bookings\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nReturn a list of the start and end time of the last 10 bookings (ordered by\nthe time at which they end, followed by the time at which they start) in the\nsystem.\n\n.. code-block:: sql\n\n   SELECT starttime, starttime + slots*(interval '30 minutes') AS endtime\n\tFROM bookings\n\tORDER BY endtime DESC, starttime DESC\n\tLIMIT 10\n\n.. code-block:: python\n\n   endtime = Booking.starttime + (Booking.slots * db.interval('30 minutes'))\n   query = (Booking\n            .select(Booking.starttime, endtime.alias('endtime'))\n            .order_by(endtime.desc(), Booking.starttime.desc())\n            .limit(10))\n\nReturn a count of bookings for each month\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nReturn a count of bookings for each month, sorted by month.\n\n.. code-block:: sql\n\n   SELECT date_trunc('month', starttime) as month, COUNT(*)\n\tFROM bookings\n\tGROUP BY month\n\tORDER BY month\n\n.. code-block:: python\n\n   month = db.truncate_date('month', Booking.starttime)\n   query = (Booking\n            .select(\n                month.alias('month'),\n                fn.COUNT(Booking.bookid).alias('count'))\n            .group_by(month)\n            .order_by(month))\n\nWork out the utilisation percentage for each facility by month\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWork out the utilisation percentage for each facility by month, sorted by name\nand month, rounded to 1 decimal place. Opening time is 8am, closing time is\n8.30pm. You can treat every month as a full month, regardless of if there were\nsome dates the club was not open.\n\n.. code-block:: sql\n\n   SELECT name, month,\n      round((100*slots)/\n         cast(\n            25*(cast((month + interval '1 month') as date)\n            - cast (month as date)) as numeric),1) as utilisation\n   FROM (\n      SELECT facs.name as name, date_trunc('month', starttime) as month, sum(slots) as slots\n      FROM bookings bks\n      INNER JOIN facilities AS facs\n         ON bks.facid = facs.facid\n      GROUP BY facs.facid, month\n   ) as _\n   ORDER BY name, month\n\n.. code-block:: python\n\n   # Create the inner query first.\n   month = db.truncate_date('month', Booking.starttime)\n   subq = (Booking\n           .select(\n               Facility.name,\n               month.alias('month'),\n               fn.SUM(Booking.slots).alias('slots'))\n           .join(Facility)\n           .group_by(Facility.facid, month))\n\n   # Expression representing the utilization.\n   utilization = fn.ROUND(\n       (100 * subq.c.slots) /\n       Cast(25 * (\n           Cast(subq.c.month + db.interval('1 month'), 'date') -\n           Cast(subq.c.month, 'date')), 'numeric'), 1)\n\n   query = (subq\n            .select_from(\n               subq.c.name,\n               subq.c.month,\n               utilization.alias('utilization'))\n            .order_by(subq.c.name, subq.c.month))\n\nString\n------\n\nFormat the names of members\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nOutput the names of all members, formatted as 'Surname, Firstname'\n\n.. code-block:: sql\n\n   SELECT surname || ', ' || firstname as name FROM members\n\n.. code-block:: python\n\n   query = Member.select(\n       (Member.surname + ', ' + Member.firstname).alias('name'))\n\nFind facilities by a name prefix\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFind all facilities whose name begins with 'Tennis'. Retrieve all columns.\n\n.. code-block:: sql\n\n   SELECT * FROM facilities WHERE name LIKE 'Tennis%';\n\n.. code-block:: python\n\n   # `startswith()` uses ILIKE (case-insensitive):\n   query = Facility.select().where(Facility.name.startswith('Tennis'))\n\n   # For case-sensitive search use LIKE explicitly:\n   query = Facility.select().where(Facility.name.like('Tennis%'))\n\nPerform a case-insensitive search\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nPerform a case-insensitive search to find all facilities whose name begins with\n'tennis'. Retrieve all columns.\n\n.. code-block:: sql\n\n   SELECT * FROM facilities WHERE name ILIKE 'tennis%';\n\n   -- OR --\n   SELECT * FROM facilities WHERE UPPER(name) LIKE 'TENNIS%';\n\n.. code-block:: python\n\n   # `startswith()` uses ILIKE (case-insensitive):\n   query = Facility.select().where(Facility.name.startswith('tennis'))\n\n   # For case-sensitive search use ILIKE explicitly:\n   query = Facility.select().where(Facility.name.ilike('tennis%'))\n\n   # Or convert to upper:\n   query = Facility.select().where(\n      fn.upper(Facility.name).like('TENNIS%'))\n\nFind telephone numbers with parentheses\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nYou've noticed that the club's member table has telephone numbers with very\ninconsistent formatting. You'd like to find all the telephone numbers that\ncontain parentheses, returning the member ID and telephone number sorted by\nmember ID.\n\n.. code-block:: sql\n\n   SELECT memid, telephone FROM members WHERE telephone ~ '[()]';\n\n.. code-block:: python\n\n   query = (Member\n            .select(Member.memid, Member.telephone)\n            .where(Member.telephone.regexp('[()]')))\n\nPad zip codes with leading zeroes\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe zip codes in our example dataset have had leading zeroes removed from them\nby virtue of being stored as a numeric type. Retrieve all zip codes from the\nmembers table, padding any zip codes less than 5 characters long with leading\nzeroes. Order by the new zip code.\n\n.. code-block:: sql\n\n   SELECT lpad(cast(zipcode as char(5)),5,'0') AS zip\n   FROM members\n   ORDER BY zip\n\n.. code-block:: python\n\n   # Because we're wrapping an integer field, Peewee will still want to try and\n   # coerce the LPAD() output to an integer, so we need to inform Peewee to\n   # leave the LPAD result as-is.\n   zipcode = fn.lpad(Cast(Member.zipcode, 'char(5)'), 5, '0', coerce=False)\n   query = Member.select(zipcode.alias('zipcode')).order_by(zipcode)\n\nAggregate by last name first initial\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nYou'd like to produce a count of how many members you have whose surname starts\nwith each letter of the alphabet. Sort by the letter, and don't worry about\nprinting out a letter if the count is 0.\n\n.. code-block:: sql\n\n   SELECT substr(surname, 1, 1) as letter, count(*) as count\n   FROM members\n   GROUP BY letter\n   ORDER BY letter\n\n.. code-block:: python\n\n   initial = fn.SUBSTR(Member.surname, 1, 1)\n   q = (Member\n        .select(initial, fn.COUNT(Member.memid))\n        .group_by(initial)\n        .order_by(initial))\n\nClean up telephone numbers\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe telephone numbers in the database are very inconsistently formatted. You'd\nlike to print a list of member ids and numbers that have had '-','(',')', and\n'' characters removed. Order by member id.\n\n.. code-block:: sql\n\n   SELECT memid, translate(telephone, '-() ', '') as telephone\n   FROM members\n   ORDER BY memid;\n\n.. code-block:: python\n\n   clean = fn.translate(Member.telephone, '-() ', '')\n   q = (Member\n        .select(Member.memid, clean.alias('telephone'))\n        .order_by(Member.memid))\n\nRecursion\n---------\n\nCommon Table Expressions allow us to, effectively, create our own temporary\ntables for the duration of a query - they're largely a convenience to help us\nmake more readable SQL. Using the WITH RECURSIVE modifier, however, it's\npossible for us to create recursive queries. This is enormously advantageous\nfor working with tree and graph-structured data - imagine retrieving all of the\nrelations of a graph node to a given depth, for example.\n\nFind the upward recommendation chain for member ID 27\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFind the upward recommendation chain for member ID 27: that is, the member\nwho recommended them, and the member who recommended that member, and so on.\nReturn member ID, first name, and surname. Order by descending member id.\n\n.. code-block:: sql\n\n    WITH RECURSIVE recommenders(recommender) as (\n      SELECT recommendedby FROM members WHERE memid = 27\n      UNION ALL\n      SELECT mems.recommendedby\n      FROM recommenders recs\n      INNER JOIN members AS mems ON mems.memid = recs.recommender\n    )\n    SELECT recs.recommender, mems.firstname, mems.surname\n    FROM recommenders AS recs\n    INNER JOIN members AS mems ON recs.recommender = mems.memid\n    ORDER By memid DESC;\n\n.. code-block:: python\n\n    # Base-case of recursive CTE. Get member recommender where memid=27.\n    base = (Member\n            .select(Member.recommendedby)\n            .where(Member.memid == 27)\n            .cte('recommenders', recursive=True, columns=('recommender',)))\n\n    # Recursive term of CTE. Get recommender of previous recommender.\n    MA = Member.alias()\n    recursive = (MA\n                 .select(MA.recommendedby)\n                 .join(base, on=(MA.memid == base.c.recommender)))\n\n    # Combine the base-case with the recursive term.\n    cte = base.union_all(recursive)\n\n    # Select from the recursive CTE, joining on member to get name info.\n    query = (cte\n             .select_from(cte.c.recommender, Member.firstname, Member.surname)\n             .join(Member, on=(cte.c.recommender == Member.memid))\n             .order_by(Member.memid.desc()))\n\nFind the downward recommendation chain for member ID 1\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFind the downward recommendation chain for member ID 1: that is, the members\nthey recommended, the members those members recommended, and so on. Return\nmember ID and name, and order by ascending member id.\n\n.. code-block:: sql\n\n   WITH RECURSIVE recommendeds(memid) AS (\n      SELECT memid FROM members WHERE recommendedby = 1\n      UNION ALL\n      SELECT mems.memid\n      FROM recommendeds recs\n      INNER JOIN members mems ON mems.recommendedby = recs.memid\n   )\n   SELECT recs.memid, mems.firstname, mems.surname\n   FROM recommendeds recs\n   INNER JOIN members mems\n      ON recs.memid = mems.memid\n   ORDER BY memid\n\n.. code-block:: python\n\n   # Base-case of recursive CTE. Get members recommended by memid=1.\n   base = (Member\n          .select(Member.memid)\n          .where(Member.recommendedby == 1)\n          .cte('recommenders', recursive=True, columns=('memid',)))\n\n   # Recursive term of CTE. Get recommended by previous recommender.\n   MA = Member.alias()\n   recursive = (MA\n               .select(MA.memid)\n               .join(base, on=(MA.recommendedby == base.c.memid)))\n\n   # Combine the base-case with the recursive term.\n   cte = base.union_all(recursive)\n\n   # Select from the recursive CTE, joining on member to get name info.\n   query = (cte\n           .select_from(cte.c.memid, Member.firstname, Member.surname)\n           .join(Member, on=(cte.c.memid == Member.memid))\n           .order_by(Member.memid))\n   for row in query:\n       print(row.memid, row.firstname, row.surname)\n\nProduce a upward recommendation chain for any member\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nProduce a CTE that can return the upward recommendation chain for any member.\nYou should be able to select recommender from recommenders where member=x.\nDemonstrate it by getting the chains for members 12 and 22. Results table\nshould have member and recommender, ordered by member ascending, recommender\ndescending.\n\n.. code-block:: sql\n\n   WITH RECURSIVE recommenders(recommender, member) AS (\n      SELECT recommendedby, memid\n      FROM members\n      UNION ALL\n      SELECT mems.recommendedby, recs.member\n      FROM recommenders recs\n      INNER JOIN members mems\n      ON mems.memid = recs.recommender\n   )\n   SELECT recs.member member, recs.recommender, mems.firstname, mems.surname\n   FROM recommenders recs\n   INNER JOIN members mems\n      ON recs.recommender = mems.memid\n   WHERE recs.member = 22 or recs.member = 12\n   ORDER BY recs.member ASC, recs.recommender DESC\n\n.. code-block:: python\n\n   # Base-case of recursive CTE. Get member recommender where memid=27.\n   base = (Member\n          .select(Member.recommendedby, Member.memid)\n          .cte('recommenders', recursive=True, columns=('recommender', 'member')))\n\n   # Recursive term of CTE. Get recommender of previous recommender.\n   MA = Member.alias()\n   recursive = (MA\n               .select(MA.recommendedby, base.c.member)\n               .join(base, on=(MA.memid == base.c.recommender)))\n\n   # Combine the base-case with the recursive term.\n   cte = base.union_all(recursive)\n\n   # Select from the recursive CTE, joining on member to get name info.\n   query = (cte\n           .select_from(\n               cte.c.member,\n               cte.c.recommender,\n               Member.firstname,\n               Member.surname)\n           .join(Member, on=(cte.c.recommender == Member.memid))\n           .where((cte.c.member == 22) | (cte.c.member == 12))\n           .order_by(cte.c.member, cte.c.recommender.desc()))\n"
  },
  {
    "path": "docs/peewee/query_operators.rst",
    "content": ".. _query-operators:\n\nQuery Operators\n===============\n\nThe following types of comparisons are supported by peewee:\n\n================ =======================================\nComparison       Meaning\n================ =======================================\n``==``           x equals y\n``!=``           x is not equal to y\n``<``            x is less than y\n``<=``           x is less than or equal to y\n``>``            x is greater than y\n``>=``           x is greater than or equal to y\n``<<``           x IN y, where y is a list or query\n``>>``           x IS y, where y is None/NULL\n``%``            x LIKE y where y may contain wildcards\n``**``           x ILIKE y where y may contain wildcards\n``^``            x XOR y\n``~``            Unary negation (e.g., NOT x)\n================ =======================================\n\nAdditional operations are provided as methods:\n\n======================= ===============================================\nMethod                  Meaning\n======================= ===============================================\n``.in_(value)``         IN lookup (identical to ``<<``).\n``.not_in(value)``      NOT IN lookup.\n``.is_null(is_null)``   IS NULL or IS NOT NULL. Accepts boolean param.\n``.contains(substr)``   Wild-card search for substring.\n``.startswith(prefix)`` Search for values beginning with ``prefix``.\n``.endswith(suffix)``   Search for values ending with ``suffix``.\n``.between(low, high)`` Search where ``low <= value <= high``.\n``.regexp(exp)``        Regular expression match (case-sensitive).\n``.iregexp(exp)``       Regular expression match (case-insensitive).\n``.bin_and(value)``     Binary AND.\n``.bin_or(value)``      Binary OR.\n``.concat(other)``      Concatenate two strings or objects using ``||``.\n``.distinct()``         Mark column for DISTINCT selection.\n``.collate(collation)`` Specify column with the given collation.\n``.cast(type)``         Cast the value of the column to the given type.\n======================= ===============================================\n\nTo combine clauses using logical operators, use:\n\n================ ==================== ======================================================\nOperator         Meaning              Example\n================ ==================== ======================================================\n``&``            AND                  ``(User.is_active == True) & (User.is_admin == True)``\n``|`` (pipe)     OR                   ``(User.is_admin) | (User.is_superuser)``\n``~``            NOT (unary negation) ``~(User.username.contains('admin'))``\n================ ==================== ======================================================\n\nHere is how you might use some of these query operators:\n\n.. code-block:: python\n\n   # Find the user whose username is \"charlie\".\n   User.select().where(User.username == 'charlie')\n\n   # Find the users whose username is in [charlie, huey, mickey]\n   User.select().where(User.username.in_(['charlie', 'huey', 'mickey']))\n\n   # Find users whose salary is between 50k and 60k (inclusive).\n   Employee.select().where(Employee.salary.between(50000, 60000))\n\n   Employee.select().where(Employee.name.startswith('C'))\n\n   Blog.select().where(Blog.title.contains(search_string))\n\nHere is how you might combine expressions. Comparisons can be arbitrarily\ncomplex.\n\n.. note::\n   Comparisons must be wrapped in parentheses due to Python operator\n   precedence rules.\n\n.. code-block:: python\n\n   # Find any users who are active administrations.\n   User.select().where(\n     (User.is_admin == True) &\n     (User.is_active == True))\n\n   # Find any users who are either administrators or super-users.\n   User.select().where(\n     (User.is_admin == True) |\n     (User.is_superuser == True))\n\n   # Alternatively, use the boolean values directly. Here we query users who\n   # are admins and NOT superusers.\n   User.select().where(User.is_admin & ~User.is_superuser)\n\n   # Find any Tweets by users who are not admins (NOT IN).\n   admins = User.select().where(User.is_admin == True)\n   non_admin_tweets = Tweet.select().where(Tweet.user.not_in(admins))\n\n   # Find any users who are not my friends (strangers).\n   friends = User.select().where(User.username.in_(['charlie', 'huey', 'mickey']))\n   strangers = User.select().where(User.id.not_in(friends))\n\n.. warning::\n   Although you may be tempted to use python's ``in``, ``and``, ``or``,\n   ``is``, and ``not`` operators in your query expressions, these **will not work.**\n   The return value of an ``in`` expression is always coerced to a boolean value.\n   Similarly, ``and``, ``or`` and ``not`` all treat their arguments as boolean\n   values and cannot be overloaded.\n\n   So just remember:\n\n   * Use ``.in_()`` and ``.not_in()`` instead of ``in`` and ``not in``\n   * Use ``&`` instead of ``and``\n   * Use ``|`` instead of ``or``\n   * Use ``~`` instead of ``not``\n   * Use ``.is_null()`` instead of ``is None`` or ``== None``.\n   * Use ``==`` and ``!=`` for comparing against ``True`` and ``False``, or\n     you may use the implicit value of the expression.\n   * **Don't forget to wrap comparisons in parentheses when using logical operators.**\n\nFor more examples, see the :ref:`expressions` section.\n\n.. note::\n   **LIKE and ILIKE with SQLite**\n\n   Because SQLite's ``LIKE`` operation is case-insensitive by default,\n   peewee will use the SQLite ``GLOB`` operation for case-sensitive searches.\n   The glob operation uses asterisks for wildcards as opposed to the usual\n   percent-sign. If you are using SQLite and want case-sensitive partial\n   string matching, remember to use asterisks for the wildcard.\n\nThree Valued Logic\n------------------\n\nBecause of the way SQL handles ``NULL``, there are some special operations\navailable for expressing:\n\n* ``IS NULL``\n* ``IS NOT NULL``\n* ``IN``\n* ``NOT IN``\n\nWhile it would be possible to use the ``IS NULL`` and ``IN`` operators with the\nnegation operator (``~``), sometimes to get the correct semantics you will need\nto explicitly use ``IS NOT NULL`` and ``NOT IN``.\n\nThe simplest way to use ``IS NULL`` and ``IN`` is to use the operator\noverloads:\n\n.. code-block:: python\n\n   # Get all User objects whose last login is NULL.\n   User.select().where(User.last_login >> None)\n\n   # Get users whose username is in the given list.\n   usernames = ['charlie', 'huey', 'mickey']\n   User.select().where(User.username << usernames)\n\nIf you don't like operator overloads, you can call the Field methods instead:\n\n.. code-block:: python\n\n   # Get all User objects whose last login is NULL.\n   User.select().where(User.last_login.is_null(True))\n\n   # Get users whose username is in the given list.\n   usernames = ['charlie', 'huey', 'mickey']\n   User.select().where(User.username.in_(usernames))\n\nTo negate the above queries, you can use unary negation, but for the correct\nsemantics use the special ``IS NOT`` and ``NOT IN`` operators:\n\n.. code-block:: python\n\n   # Get all User objects whose last login is *NOT* NULL.\n   User.select().where(User.last_login.is_null(False))\n\n   # Get users whose username is *NOT* in the given list.\n   usernames = ['charlie', 'huey', 'mickey']\n   User.select().where(User.username.not_in(usernames))\n\n.. _custom-operators:\n\nUser-Defined Operators\n-----------------------\n\nBecause I ran out of python operators to overload, there are some missing\noperators in peewee, for instance ``modulo``. If you find that you need to\nsupport an operator that is not in the table above, it is very easy to add your\nown.\n\nHere is how you might add support for ``modulo`` in SQLite:\n\n.. code-block:: python\n\n   from peewee import *\n   from peewee import Expression  # The building block for expressions.\n\n   def mod(lhs, rhs):\n       # Note: this works with Sqlite, but some drivers may use string-\n       # formatting before sending the query to the database, so you may\n       # need to use '%%' instead here.\n       return Expression(lhs, '%', rhs)\n\nNow you can use these custom operators to build richer queries:\n\n.. code-block:: python\n\n   # Users with even ids.\n   User.select().where(mod(User.id, 2) == 0)\n\n.. _expressions:\n\nExpressions\n-----------\n\nPeewee is designed to provide a simple, expressive, and pythonic way of\nconstructing SQL queries. This section will provide a quick overview of some\ncommon types of expressions.\n\nTwo common types of objects that are composed to create expressions:\n\n* :class:`Field` instances\n* SQL aggregations and functions using :class:`fn`\n\nWe will assume a simple \"User\" model with fields for username and other things.\nIt looks like this:\n\n.. code-block:: python\n\n   class User(Model):\n       username = CharField()\n       is_admin = BooleanField()\n       is_active = BooleanField()\n       last_login = DateTimeField()\n       login_count = IntegerField()\n       failed_logins = IntegerField()\n\nComparisons use the :ref:`query-operators`:\n\n.. code-block:: python\n\n   # username is equal to 'charlie'\n   User.username == 'charlie'\n\n   # user has logged in less than 5 times\n   User.login_count < 5\n\nComparisons can be combined using **bitwise** *and* and *or*. Operator\nprecedence is controlled by python and comparisons can be nested to an\narbitrary depth:\n\n.. code-block:: python\n\n   # User is both and admin and has logged in today\n   (User.is_admin == True) & (User.last_login >= today)\n\n   # User's username is either charlie or charles\n   (User.username == 'charlie') | (User.username == 'charles')\n\n   # User is active and not a superuser.\n   (User.is_active & ~User.is_superuser)\n\nComparisons can be used with functions as well:\n\n.. code-block:: python\n\n   # user's username starts with a 'g' or a 'G':\n   fn.Lower(fn.Substr(User.username, 1, 1)) == 'g'\n\nWe can do some fairly interesting things, as expressions can be compared\nagainst other expressions. Expressions also support arithmetic operations:\n\n.. code-block:: python\n\n   # users who entered the incorrect more than half the time and have logged\n   # in at least 10 times\n   (User.failed_logins > (User.login_count * .5)) & (User.login_count > 10)\n\nExpressions allow us to do *atomic updates*:\n\n.. code-block:: python\n\n   # when a user logs in we want to increment their login count:\n   User.update(login_count=User.login_count + 1).where(User.id == user_id)\n\nExpressions can be used in all parts of a query, so experiment!\n\nRow values\n^^^^^^^^^^\n\nMany databases support `row values <https://www.sqlite.org/rowvalue.html>`_,\nwhich are similar to Python `tuple` objects. In Peewee, it is possible to use\nrow-values in expressions via :class:`Tuple`. For example,\n\n.. code-block:: python\n\n   # If for some reason your schema stores dates in separate columns (\"year\",\n   # \"month\" and \"day\"), you can use row-values to find all rows that happened\n   # in a given month:\n   Tuple(Event.year, Event.month) == (2019, 1)\n\nThe more common use for row-values is to compare against multiple columns from\na subquery in a single expression. There are other ways to express these types\nof queries, but row-values may offer a concise and readable approach.\n\nFor example, assume we have a table \"EventLog\" which contains an event type, an\nevent source, and some metadata. We also have an \"IncidentLog\", which has\nincident type, incident source, and metadata columns. We can use row-values to\ncorrelate incidents with certain events:\n\n.. code-block:: python\n\n   class EventLog(Model):\n       event_type = TextField()\n       source = TextField()\n       data = TextField()\n       timestamp = TimestampField()\n\n   class IncidentLog(Model):\n       incident_type = TextField()\n       source = TextField()\n       traceback = TextField()\n       timestamp = TimestampField()\n\n   # Get a list of all the incident types and sources that have occured today.\n   incidents = (IncidentLog\n                .select(IncidentLog.incident_type, IncidentLog.source)\n                .where(IncidentLog.timestamp >= datetime.date.today()))\n\n   # Find all events that correlate with the type and source of the\n   # incidents that occured today.\n   events = (EventLog\n             .select()\n             .where(Tuple(EventLog.event_type, EventLog.source).in_(incidents))\n             .order_by(EventLog.timestamp))\n\nOther ways to express this type of query would be to use a :ref:`join <relationships>`\nor to :ref:`join on a subquery <join-subquery>`. The above example is there\njust to give you and idea how :class:`Tuple` might be used.\n\nYou can also use row-values to update multiple columns in a table, when the new\ndata is derived from a subquery. For an example, see `here <https://www.sqlite.org/rowvalue.html#update_multiple_columns_of_a_table_based_on_a_query>`_.\n\nSQL Functions\n-------------\n\nSQL functions, like ``COUNT()`` or ``SUM()``, can be expressed using the\n:func:`fn` helper:\n\n.. code-block:: python\n\n   # Get all users and the number of tweets they've authored. Sort the\n   # results from most tweets -> fewest tweets.\n   query = (User\n            .select(User, fn.COUNT(Tweet.id).alias('tweet_count'))\n            .join(Tweet, JOIN.LEFT_OUTER)\n            .group_by(User)\n            .order_by(fn.COUNT(Tweet.id).desc()))\n   for user in query:\n       print('%s -- %s tweets' % (user.username, user.tweet_count))\n\nThe ``fn`` helper exposes any SQL function as if it were a method. The\nparameters can be fields, values, subqueries, or even nested functions.\n\nNesting function calls\n^^^^^^^^^^^^^^^^^^^^^^\n\nSuppose you need to want to get a list of all users whose username begins with\n*a*. There are a couple ways to do this, but one method might be to use some\nSQL functions like *LOWER* and *SUBSTR*. To use arbitrary SQL functions, use\nthe special :func:`fn` object to construct queries:\n\n.. code-block:: python\n\n   # Select the user's id, username and the first letter of their username, lower-cased\n   first_letter = fn.LOWER(fn.SUBSTR(User.username, 1, 1))\n   query = User.select(User, first_letter.alias('first_letter'))\n\n   # Alternatively we could select only users whose username begins with 'a'\n   a_users = User.select().where(first_letter == 'a')\n\n   for user in a_users:\n       print(user.username)\n\nSQL Helper\n----------\n\nThere are times when you may want to simply pass in some arbitrary sql. You can\ndo this using the special :class:`SQL` class. One use-case is when\nreferencing an alias:\n\n.. code-block:: python\n\n   # We'll query the user table and annotate it with a count of tweets for\n   # the given user\n   query = (User\n            .select(User, fn.Count(Tweet.id).alias('ct'))\n            .join(Tweet)\n            .group_by(User))\n\n   # Now we will order by the count, which was aliased to \"ct\"\n   query = query.order_by(SQL('ct'))\n\n   # You could, of course, also write this as:\n   query = query.order_by(fn.COUNT(Tweet.id))\n\nThere are two ways to execute hand-crafted SQL statements with peewee:\n\n1. :meth:`Database.execute_sql` for executing any type of query\n2. :class:`RawQuery` for executing ``SELECT`` queries and returning model\n   instances.\n\nSecurity and SQL Injection\n--------------------------\n\nBy default peewee will parameterize queries, so any parameters passed in by the\nuser will be escaped. The only exception to this rule is if you are writing a\nraw SQL query or are passing in a ``SQL`` object which may contain untrusted\ndata. To mitigate this, ensure that any user-defined data is passed in as a\nquery parameter and not part of the actual SQL query:\n\n.. code-block:: python\n\n   # Bad! DO NOT DO THIS!\n   query = MyModel.raw('SELECT * FROM my_table WHERE data = %s' % user_data)\n\n   # Bad! DO NOT DO THIS!\n   query = MyModel.select().where(SQL('Some SQL expression %s' % user_data))\n\nUse parameters to prevent SQL injection:\n\n.. code-block:: python\n\n   # Good. `user_data` will be treated as a parameter to the query.\n   query = MyModel.raw('SELECT * FROM my_table WHERE data = %s', user_data)\n\n   # Good. `user_data` will be treated as a parameter.\n   query = MyModel.select().where(SQL('Some SQL expression %s', user_data))\n\n.. note::\n   MySQL and Postgresql use ``'%s'`` to denote parameters. SQLite, on the\n   other hand, uses ``'?'``. Be sure to use the character appropriate to your\n   database. You can also find this parameter by checking\n   :attr:`Database.param`.\n"
  },
  {
    "path": "docs/peewee/querying.rst",
    "content": ".. _querying:\n\nQuerying\n========\n\nThis document covers reading data from the database: SELECT queries, filtering,\nsorting, aggregation, and result-set iteration. Writing data (INSERT, UPDATE,\nDELETE) is covered in :ref:`writing`.\n\nAll examples use the following models (see :ref:`models`):\n\n.. code-block:: python\n\n   import datetime\n   from peewee import *\n\n   db = SqliteDatabase(':memory:')\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = TextField(unique=True)\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(default=datetime.datetime.now)\n       is_published = BooleanField(default=True)\n\n\n.. seealso:: :ref:`Extensive library of SQL / Peewee examples <query-library>`\n\nSelecting Records\n-----------------\n\n:meth:`Model.select` returns a :class:`Select` query. The query is lazy - the\ndatabase is not queried until you iterate over the result, index, slice or call\na method that forces execution.\n\n.. code-block:: python\n\n   # All users. No query issued yet.\n   query = User.select()\n\n   # Query executes here.\n   for user in query:\n       print(user.username)\n\nIterating over the same query object a second time does not re-query the\ndatabase: results are cached on the query object. To disable caching (for\nexample, when iterating over a large result set), use :meth:`~BaseQuery.iterator`:\n\n.. code-block:: python\n\n   for user in User.select().iterator():\n       process(user)   # One row at a time, not cached.\n\nTo select specific columns rather than all columns, pass field expressions to\n``select()``:\n\n.. code-block:: python\n\n   for user in User.select(User.username):\n       print(user.username)\n       # user.id is not populated - it was not selected.\n\nTo select columns from multiple models, pass both model classes or their\nfields. Peewee reconstructs the model graph from the result set:\n\n.. code-block:: python\n\n   query = Tweet.select(Tweet, User).join(User)\n   for tweet in query:\n       # tweet.user is a fully populated User instance.\n       # No extra query is issued.\n       print(tweet.user.username, '->', tweet.content)\n\n.. seealso::\n   :ref:`relationships` covers joins in detail.\n\n\nRetrieving a Single Record\n--------------------------\n\n:meth:`Model.get` executes the query and returns the first matching row.\nIf no row matches, :exc:`~Model.DoesNotExist` is raised:\n\n.. code-block:: python\n\n   user = User.get(User.username == 'charlie')\n\n   # Equivalent long form:\n   user = User.select().where(User.username == 'charlie').get()\n\n:meth:`~Model.get_by_id` and the subscript operator are shortcuts for\nprimary-key lookups:\n\n.. code-block:: python\n\n   user = User.get_by_id(1)\n   user = User[1]             # Same.\n\n:meth:`~Model.get_or_none` returns ``None`` instead of raising an exception\nwhen no row is found:\n\n.. code-block:: python\n\n   user = User.get_or_none(User.username == 'charlie')\n   if user is None:\n       print('Not found.')\n\n:meth:`~SelectBase.first` returns the first row of a query, or ``None``:\n\n.. code-block:: python\n\n   latest = Tweet.select().order_by(Tweet.timestamp.desc()).first()\n\nGet or Create\n^^^^^^^^^^^^^\n\n:meth:`~Model.get_or_create` retrieves a matching row, or creates it if it\ndoes not exist. It returns a ``(instance, created)`` tuple:\n\n.. code-block:: python\n\n   user, created = User.get_or_create(username='charlie')\n   if created:\n       print('New user created.')\n\nUse the ``defaults`` keyword to supply values that are only used during\ncreation, not as lookup keys:\n\n.. code-block:: python\n\n   user, created = User.get_or_create(\n       username='charlie',\n       defaults={'joined': datetime.date.today()})\n\nWhen uniqueness is enforced by a database constraint, the recommended pattern\nis to attempt creation first and fall back to retrieval on failure:\n\n.. code-block:: python\n\n   try:\n       with db.atomic():\n           return User.create(username=username)\n   except IntegrityError:\n       return User.get(User.username == username)\n\nThis avoids a race window between the lookup and the insert.\n\n.. _filtering:\n\nFiltering\n---------\n\n:meth:`~Query.where` accepts expressions built from field comparisons. Peewee\noverloads Python's comparison operators to produce SQL expressions:\n\n.. code-block:: python\n\n   # Equality\n   User.select().where(User.username == 'charlie')\n\n   # Inequality\n   Tweet.select().where(Tweet.is_published != False)\n\n   # Comparison\n   Tweet.select().where(Tweet.timestamp < datetime.datetime(2024, 1, 1))\n\nPeewee uses **bitwise** operators (``&`` and ``|``) rather than logical\noperators (``and`` and ``or``). The reason for this is that Python coerces\nthe logical operations to a boolean value. This is also the reason why \"IN\"\nqueries must be expressed using ``.in_()`` rather than the ``in`` operator.\n\n.. seealso:: :ref:`Query operations <query-operators>` to see all operators.\n\nCombine conditions with ``&`` (AND) and ``|`` (OR):\n\n.. code-block:: python\n\n   # Published tweets by charlie:\n   query = (Tweet\n            .select()\n            .join(User)\n            .where(\n                (User.username == 'charlie') &\n                (Tweet.is_published == True)))\n\n   # Tweets by charlie OR huey:\n   query = (Tweet\n            .select()\n            .join(User)\n            .where(\n                (User.username == 'charlie') |\n                (User.username == 'huey')))\n\nNegate a condition with ``~``:\n\n.. code-block:: python\n\n   # All users except charlie:\n   User.select().where(~(User.username == 'charlie'))\n\nCalling ``.where()`` multiple times on a query ANDs the conditions:\n\n.. code-block:: python\n\n   # Equivalent to WHERE is_published = 1 AND timestamp > ...\n   query = (Tweet\n            .select()\n            .where(Tweet.is_published == True)\n            .where(Tweet.timestamp > one_week_ago))\n\nCommon filtering methods\n^^^^^^^^^^^^^^^^^^^^^^^^\n\n============================================= ====================================\nMethod                                        SQL equivalent\n============================================= ====================================\n``User.username == 'charlie'``                ``username = 'charlie'``\n``User.username != 'charlie'``                ``username != 'charlie'``\n``Tweet.timestamp < dt``                      ``timestamp < dt``\n``Tweet.timestamp >= dt``                     ``timestamp >= dt``\n``Tweet.timestamp.between(start, end)``       ``timestamp BETWEEN start AND end``\n``User.username.in_(['a', 'b'])``             ``username IN ('a', 'b')``\n``User.username.not_in(['a', 'b'])``          ``username NOT IN ...``\n``User.username.contains('char')``            ``username LIKE '%char%'``\n``User.username.startswith('ch')``            ``username LIKE 'ch%'``\n``User.username.endswith('ie')``              ``username LIKE '%ie'``\n``User.username.regexp(r'^[a-z]+$')``         ``username REGEXP ...``\n``User.username.is_null()``                   ``username IS NULL``\n``User.username.is_null(False)``              ``username IS NOT NULL``\n============================================= ====================================\n\n.. note::\n   ``IN`` queries must use ``.in_()`` rather than Python's ``in`` operator,\n   because Python's ``in`` returns a boolean and cannot be overridden.\n\n.. seealso::\n   :ref:`query-operators` for the full list of supported operators and methods.\n\nSQL functions\n^^^^^^^^^^^^^\n\nThe :class:`fn` helper calls any SQL function by name:\n\n.. code-block:: python\n\n   from peewee import fn\n\n   # Users whose username starts with a vowel (case-insensitive).\n   vowels = ('a', 'e', 'i', 'o', 'u')\n   query = User.select().where(\n       fn.LOWER(fn.SUBSTR(User.username, 1, 1)).in_(vowels))\n\n   # Tweets whose content is less than 10 characters long.\n   query = Tweet.select().where(\n       fn.LENGTH(Tweet.content) < 10)\n\n\nSorting\n-------\n\n:meth:`~Query.order_by` specifies the column(s) to sort by:\n\n.. code-block:: python\n\n   # Ascending (default).\n   Tweet.select().order_by(Tweet.timestamp)\n\n   # Descending.\n   Tweet.select().order_by(Tweet.timestamp.desc())\n\n   # Using the + / - prefix operators:\n   Tweet.select().order_by(+Tweet.timestamp)     # Ascending.\n   Tweet.select().order_by(-Tweet.timestamp)     # Descending.\n\nSort on multiple columns by passing multiple arguments:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select()\n            .join(User)\n            .order_by(User.username, Tweet.timestamp.desc()))\n\nSorting by a calculated or aliased value\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen ordering by an aggregate or expression that appears in ``select()``,\nreference it by re-using the expression or by wrapping the alias in\n:class:`SQL`:\n\n.. code-block:: python\n\n   tweet_count = fn.COUNT(Tweet.id)\n\n   query = (User\n            .select(User.username, tweet_count.alias('num_tweets'))\n            .join(Tweet, JOIN.LEFT_OUTER)\n            .group_by(User.username)\n            .order_by(tweet_count.desc()))\n\n   # Alternatively, reference the alias string via SQL():\n   query = (User\n            .select(User.username, fn.COUNT(Tweet.id).alias('num_tweets'))\n            .join(Tweet, JOIN.LEFT_OUTER)\n            .group_by(User.username)\n            .order_by(SQL('num_tweets').desc()))\n\nRandom ordering\n^^^^^^^^^^^^^^^\n\n:meth:`Database.random` provides a database-specific implementation of the\nrandom function, which can be used for ordering:\n\n.. code-block:: python\n\n   # Select 5 random winners.\n   LotteryEntry.select().order_by(db.random()).limit(5)\n\n\nPagination, Limiting, and Offsetting\n--------------------------------------\n\n:meth:`~Query.limit` and :meth:`~Query.offset` map directly to SQL:\n\n.. code-block:: python\n\n   # First 10 rows.\n   Tweet.select().order_by(Tweet.id).limit(10)\n\n   # Rows 11-20.\n   Tweet.select().order_by(Tweet.id).limit(10).offset(10)\n\n:meth:`~Query.paginate` is a convenience wrapper:\n\n.. code-block:: python\n\n   # Page 3, 20 items per page (rows 41-60).\n   Tweet.select().order_by(Tweet.id).paginate(3, 20)\n\n.. attention::\n   Page numbers are 1-based. Page 1 returns the first ``items_per_page`` rows.\n\n\nCounting\n--------\n\n:meth:`~SelectBase.count` wraps the query in a ``SELECT COUNT(1) FROM (...)``\nand returns an integer:\n\n.. code-block:: python\n\n   total = Tweet.select().count()\n   published = Tweet.select().where(Tweet.is_published == True).count()\n\n\nAggregates and GROUP BY\n------------------------\n\nUse :class:`fn` to call aggregate functions and :meth:`~Select.group_by`\nto group:\n\n.. code-block:: python\n\n   query = (User\n            .select(User.username, fn.COUNT(Tweet.id).alias('tweet_count'))\n            .join(Tweet, JOIN.LEFT_OUTER)\n            .group_by(User.username)\n            .order_by(SQL('tweet_count').desc()))\n\n   for user in query:\n       print(user.username, user.tweet_count)\n\nFilter groups with :meth:`~Select.having`:\n\n.. code-block:: python\n\n   # Users with more than 5 published tweets.\n   query = (User\n            .select(User.username, fn.COUNT(Tweet.id).alias('n'))\n            .join(Tweet)\n            .where(Tweet.is_published == True)\n            .group_by(User.username)\n            .having(fn.COUNT(Tweet.id) > 5))\n\n\nScalar Values\n-------------\n\n:meth:`~SelectBase.scalar` executes a query and returns the first column of the\nfirst row as a Python value. Use it when a query produces a single number or\nstring:\n\n.. code-block:: python\n\n   oldest = Tweet.select(fn.MIN(Tweet.timestamp)).scalar()\n   distinct_users = Tweet.select(fn.COUNT(Tweet.user.distinct())).scalar()\n\nPass ``as_tuple=True`` to retrieve multiple scalar columns:\n\n.. code-block:: python\n\n   min_ts, max_ts = Tweet.select(\n       fn.MIN(Tweet.timestamp),\n       fn.MAX(Tweet.timestamp)\n   ).scalar(as_tuple=True)\n\n.. _row-types:\n\nRow Types\n---------\n\nBy default, SELECT queries return model instances. Four alternative row types\nare available by chaining a method before iteration:\n\n* :meth:`~BaseQuery.dicts`\n* :meth:`~BaseQuery.tuples`\n* :meth:`~BaseQuery.namedtuples`\n* :meth:`~BaseQuery.objects`\n\nExample:\n\n.. code-block:: python\n\n   # Dictionaries.\n   for row in User.select().dicts():\n       print(row)   # {'id': 1, 'username': 'charlie'}\n\n   # Tuples.\n   for row in User.select().tuples():\n       print(row)   # (1, 'charlie')\n\n   # Named tuples.\n   for row in User.select().namedtuples():\n       print(row.username)\n\n   # Flatten any related data and return model instances.\n   for row in User.select().objects():\n       print(row.username)\n\n   # Or pass a constructor callable.\n   for row in User.select().objects(MyUserClass):\n       print(row.my_username)\n\nUsing tuples or dicts instead of model instances is faster for queries that\nproduce many rows, because Peewee skips constructing model objects.\n\n``objects()`` without an argument returns model instances but does not\nreconstruct the model graph from joined data, assigning all columns directly\nonto the primary model. This avoids the overhead of graph reconstruction when\nyou have joined data and don't need nested model instances.\n\n.. _large-results:\n\nIterating Over Large Result Sets\n----------------------------------\n\nFor queries returning many rows, disable result caching with\n:meth:`~BaseQuery.iterator` to keep memory usage flat:\n\n.. code-block:: python\n\n   # Combine iterator() with tuples() for maximum throughput.\n   query = (Stat\n            .select()\n            .tuples()\n            .iterator())\n\n   for stat_tuple in query:\n       write_to_file(stat_tuple)\n\nWhen iterating over joined queries with ``.iterator()``, use ``.objects()``\nto avoid the overhead of model-graph reconstruction per row:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select(Tweet.content, User.username)\n            .join(User)\n            .objects()\n            .iterator())\n\n   for tweet in query:\n       print(tweet.username, tweet.content)\n\nFor maximum performance, execute the query and iterate the cursor directly:\n\n.. code-block:: python\n\n   query = Tweet.select(Tweet.content, User.username).join(User)\n   cursor = db.execute(query)\n   for content, username in cursor:\n       print(username, '->', content)\n\n\n.. _window-functions:\n\nWindow Functions\n----------------\n\nA :class:`Window` function refers to an aggregate function that operates on\na sliding window of data that is being processed as part of a ``SELECT`` query.\nWindow functions make it possible to do things like:\n\n1. Perform aggregations against subsets of a result-set.\n2. Calculate a running total.\n3. Rank results.\n4. Compare a row value to a value in the preceding (or succeeding!) row(s).\n\npeewee comes with support for SQL window functions, which can be created by\ncalling :meth:`Function.over` and passing in your partitioning or ordering\nparameters.\n\nFor the following examples, we'll use the following model and sample data:\n\n.. code-block:: python\n\n   class Sample(Model):\n       counter = IntegerField()\n       value = FloatField()\n\n   data = [(1, 10),\n           (1, 20),\n           (2, 1),\n           (2, 3),\n           (3, 100)]\n   Sample.insert_many(data, fields=[Sample.counter, Sample.value]).execute()\n\nOur sample table now contains:\n\n=== ======== ======\nid  counter  value\n=== ======== ======\n1   1        10.0\n2   1        20.0\n3   2        1.0\n4   2        3.0\n5   3        100.0\n=== ======== ======\n\nOrdered Windows\n^^^^^^^^^^^^^^^\n\nLet's calculate a running sum of the ``value`` field. In order for it to be a\n\"running\" sum, we need it to be ordered, so we'll order with respect to the\nSample's ``id`` field:\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.SUM(Sample.value).over(order_by=[Sample.id]).alias('total'))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.total)\n\n   # 1    10.    10.\n   # 1    20.    30.\n   # 2     1.    31.\n   # 2     3.    34.\n   # 3   100    134.\n\nFor another example, we'll calculate the difference between the current value\nand the previous value, when ordered by the ``id``:\n\n.. code-block:: python\n\n   difference = Sample.value - fn.LAG(Sample.value, 1).over(order_by=[Sample.id])\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       difference.alias('diff'))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.diff)\n\n   # 1    10.   NULL\n   # 1    20.    10.  -- (20 - 10)\n   # 2     1.   -19.  -- (1 - 20)\n   # 2     3.     2.  -- (3 - 1)\n   # 3   100     97.  -- (100 - 3)\n\nPartitioned Windows\n^^^^^^^^^^^^^^^^^^^\n\nLet's calculate the average ``value`` for each distinct \"counter\" value. Notice\nthat there are three possible values for the ``counter`` field (1, 2, and 3).\nWe can do this by calculating the ``AVG()`` of the ``value`` column over a\nwindow that is partitioned depending on the ``counter`` field:\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.AVG(Sample.value).over(partition_by=[Sample.counter]).alias('cavg'))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.cavg)\n\n   # 1    10.    15.\n   # 1    20.    15.\n   # 2     1.     2.\n   # 2     3.     2.\n   # 3   100    100.\n\nWe can use ordering within partitions by specifying both the ``order_by`` and\n``partition_by`` parameters. For an example, let's rank the samples by value\nwithin each distinct ``counter`` group.\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.RANK().over(\n           order_by=[Sample.value],\n           partition_by=[Sample.counter]).alias('rank'))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.rank)\n\n   # 1    10.    1\n   # 1    20.    2\n   # 2     1.    1\n   # 2     3.    2\n   # 3   100     1\n\nBounded Windows\n^^^^^^^^^^^^^^^\n\nBy default, window functions are evaluated using an *unbounded preceding* start\nfor the window, and the *current row* as the end. We can change the bounds of\nthe window our aggregate functions operate on by specifying a ``start`` and/or\n``end`` in the call to :meth:`Function.over`. Additionally, Peewee comes\nwith helper-methods on the :class:`Window` object for generating the\nappropriate boundary references:\n\n* :attr:`Window.CURRENT_ROW` - attribute that references the current row.\n* :meth:`Window.preceding` - specify number of row(s) preceding, or omit\n  number to indicate **all** preceding rows.\n* :meth:`Window.following` - specify number of row(s) following, or omit\n  number to indicate **all** following rows.\n\nTo examine how boundaries work, we'll calculate a running total of the\n``value`` column, ordered with respect to ``id``, **but** we'll only look the\nrunning total of the current row and it's two preceding rows:\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.SUM(Sample.value).over(\n           order_by=[Sample.id],\n           start=Window.preceding(2),\n           end=Window.CURRENT_ROW).alias('rsum'))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.rsum)\n\n   # 1    10.    10.\n   # 1    20.    30.  -- (20 + 10)\n   # 2     1.    31.  -- (1 + 20 + 10)\n   # 2     3.    24.  -- (3 + 1 + 20)\n   # 3   100    104.  -- (100 + 3 + 1)\n\nTechnically we did not need to specify the ``end=Window.CURRENT`` because\nthat is the default. It was shown in the example for demonstration.\n\nLet's look at another example. In this example we will calculate the \"opposite\"\nof a running total, in which the total sum of all values is decreased by the\nvalue of the samples, ordered by ``id``. To accomplish this, we'll calculate\nthe sum from the current row to the last row.\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.SUM(Sample.value).over(\n           order_by=[Sample.id],\n           start=Window.CURRENT_ROW,\n           end=Window.following()).alias('rsum'))\n\n   # 1    10.   134.  -- (10 + 20 + 1 + 3 + 100)\n   # 1    20.   124.  -- (20 + 1 + 3 + 100)\n   # 2     1.   104.  -- (1 + 3 + 100)\n   # 2     3.   103.  -- (3 + 100)\n   # 3   100    100.  -- (100)\n\nFiltered Aggregates\n^^^^^^^^^^^^^^^^^^^\n\nAggregate functions may also support filter functions (Postgres and Sqlite\n3.25+), which get translated into a ``FILTER (WHERE...)`` clause. Filter\nexpressions are added to an aggregate function with the\n:meth:`Function.filter` method.\n\nFor an example, we will calculate the running sum of the ``value`` field with\nrespect to the ``id``, but we will filter-out any samples whose ``counter=2``.\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.SUM(Sample.value).filter(Sample.counter != 2).over(\n           order_by=[Sample.id]).alias('csum'))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.csum)\n\n   # 1    10.    10.\n   # 1    20.    30.\n   # 2     1.    30.\n   # 2     3.    30.\n   # 3   100    130.\n\nThe call to :meth:`~Function.filter` must precede the call to\n:meth:`~Function.over`.\n\nReusing Window Definitions\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIf you intend to use the same window definition for multiple aggregates, you\ncan create a :class:`Window` object. The :class:`Window` object takes the\nsame parameters as :meth:`Function.over`, and can be passed to the\n``over()`` method in-place of the individual parameters.\n\nHere we'll declare a single window, ordered with respect to the sample ``id``,\nand call several window functions using that window definition:\n\n.. code-block:: python\n\n   win = Window(order_by=[Sample.id])\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.LEAD(Sample.value).over(win),\n       fn.LAG(Sample.value).over(win),\n       fn.SUM(Sample.value).over(win)\n   ).window(win)  # Include our window definition in query.\n\n   for row in query.tuples():\n       print(row)\n\n   # counter  value  lead()  lag()  sum()\n   # 1          10.     20.   NULL    10.\n   # 1          20.      1.    10.    30.\n   # 2           1.      3.    20.    31.\n   # 2           3.    100.     1.    34.\n   # 3         100.    NULL     3.   134.\n\nMultiple Window Definitions\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn the previous example, we saw how to declare a :class:`Window` definition\nand re-use it for multiple different aggregations. You can include as many\nwindow definitions as you need in your queries, but it is necessary to ensure\neach window has a unique alias:\n\n.. code-block:: python\n\n   w1 = Window(order_by=[Sample.id]).alias('w1')\n   w2 = Window(partition_by=[Sample.counter]).alias('w2')\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.SUM(Sample.value).over(w1).alias('rsum'),  # Running total.\n       fn.AVG(Sample.value).over(w2).alias('cavg')   # Avg per category.\n   ).window(w1, w2)  # Include our window definitions.\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.rsum, sample.cavg)\n\n   # counter  value   rsum     cavg\n   # 1          10.     10.     15.\n   # 1          20.     30.     15.\n   # 2           1.     31.      2.\n   # 2           3.     34.      2.\n   # 3         100     134.    100.\n\nSimilarly, if you have multiple window definitions that share similar\ndefinitions, it is possible to extend a previously-defined window definition.\nFor example, here we will be partitioning the data-set by the counter value, so\nwe'll be doing our aggregations with respect to the counter. Then we'll define\na second window that extends this partitioning, and adds an ordering clause:\n\n.. code-block:: python\n\n   w1 = Window(partition_by=[Sample.counter]).alias('w1')\n\n   # By extending w1, this window definition will also be partitioned\n   # by \"counter\".\n   w2 = Window(extends=w1, order_by=[Sample.value.desc()]).alias('w2')\n\n   query = (Sample\n            .select(Sample.counter, Sample.value,\n                    fn.SUM(Sample.value).over(w1).alias('group_sum'),\n                    fn.RANK().over(w2).alias('revrank'))\n            .window(w1, w2)\n            .order_by(Sample.id))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.group_sum, sample.revrank)\n\n   # counter  value   group_sum   revrank\n   # 1        10.     30.         2\n   # 1        20.     30.         1\n   # 2        1.      4.          2\n   # 2        3.      4.          1\n   # 3        100.    100.        1\n\n.. _window-frame-types:\n\nFrame Types: RANGE vs ROWS vs GROUPS\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nDepending on the frame type, the database will process ordered groups\ndifferently. Let's create two additional ``Sample`` rows to visualize the\ndifference:\n\n.. code-block:: pycon\n\n   >>> Sample.create(counter=1, value=20.)\n   <Sample 6>\n   >>> Sample.create(counter=2, value=1.)\n   <Sample 7>\n\nOur table now contains:\n\n=== ======== ======\nid  counter  value\n=== ======== ======\n1   1        10.0\n2   1        20.0\n3   2        1.0\n4   2        3.0\n5   3        100.0\n6   1        20.0\n7   2        1.0\n=== ======== ======\n\nLet's examine the difference by calculating a \"running sum\" of the samples,\nordered with respect to the ``counter`` and ``value`` fields. To specify the\nframe type, we can use either:\n\n* :attr:`Window.RANGE`\n* :attr:`Window.ROWS`\n* :attr:`Window.GROUPS`\n\nThe behavior of :attr:`~Window.RANGE`, when there are logical duplicates,\nmay lead to unexpected results:\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.SUM(Sample.value).over(\n           order_by=[Sample.counter, Sample.value],\n           frame_type=Window.RANGE).alias('rsum'))\n\n   for sample in query.order_by(Sample.counter, Sample.value):\n       print(sample.counter, sample.value, sample.rsum)\n\n   # counter  value   rsum\n   # 1          10.     10.\n   # 1          20.     50.\n   # 1          20.     50.\n   # 2           1.     52.\n   # 2           1.     52.\n   # 2           3.     55.\n   # 3         100     155.\n\nWith the inclusion of the new rows we now have some rows that have duplicate\n``category`` and ``value`` values. The :attr:`~Window.RANGE` frame type\ncauses these duplicates to be evaluated together rather than separately.\n\nThe more expected result can be achieved by using :attr:`~Window.ROWS` as\nthe frame-type:\n\n.. code-block:: python\n\n   query = Sample.select(\n       Sample.counter,\n       Sample.value,\n       fn.SUM(Sample.value).over(\n           order_by=[Sample.counter, Sample.value],\n           frame_type=Window.ROWS).alias('rsum'))\n\n   for sample in query.order_by(Sample.counter, Sample.value):\n       print(sample.counter, sample.value, sample.rsum)\n\n   # counter  value   rsum\n   # 1          10.     10.\n   # 1          20.     30.\n   # 1          20.     50.\n   # 2           1.     51.\n   # 2           1.     52.\n   # 2           3.     55.\n   # 3         100     155.\n\nPeewee uses these rules for determining what frame-type to use:\n\n* If the user specifies a ``frame_type``, that frame type will be used.\n* If ``start`` and/or ``end`` boundaries are specified Peewee will default to\n  using ``ROWS``.\n* If the user did not specify frame type or start/end boundaries, Peewee will\n  use the database default, which is ``RANGE``.\n\nThe :attr:`Window.GROUPS` frame type looks at the window range specification\nin terms of groups of rows, based on the ordering term(s). Using ``GROUPS``, we\ncan define the frame so it covers distinct groupings of rows. Let's look at an\nexample:\n\n.. code-block:: python\n\n   query = (Sample\n            .select(Sample.counter, Sample.value,\n                    fn.SUM(Sample.value).over(\n                       order_by=[Sample.counter, Sample.value],\n                       frame_type=Window.GROUPS,\n                       start=Window.preceding(1)).alias('gsum'))\n            .order_by(Sample.counter, Sample.value))\n\n   for sample in query:\n       print(sample.counter, sample.value, sample.gsum)\n\n   #  counter   value    gsum\n   #  1         10       10\n   #  1         20       50\n   #  1         20       50   (10) + (20+20)\n   #  2         1        42\n   #  2         1        42   (20+20) + (1+1)\n   #  2         3        5    (1+1) + 3\n   #  3         100      103  (3) + 100\n\nAs you can hopefully infer, the window is grouped by its ordering term, which\nis ``(counter, value)``. We are looking at a window that extends between one\nprevious group and the current group.\n\nFor information about the window function APIs, see:\n\n* :meth:`Function.over`\n* :meth:`Function.filter`\n* :class:`Window`\n\nFor general information on window functions, read the postgres `window functions tutorial <https://www.postgresql.org/docs/current/tutorial-window.html>`_\n\nAdditionally, the `postgres docs <https://www.postgresql.org/docs/current/sql-select.html#SQL-WINDOW>`_\nand the `sqlite docs <https://www.sqlite.org/windowfunctions.html>`_\ncontain a lot of good information.\n\n.. _cte:\n\nCommon Table Expressions\n------------------------\n\nA CTE factors out a subquery and gives it a name, making complex queries more\nreadable and sometimes more efficient. CTEs also support recursion.\n\nDefine a CTE with :meth:`~SelectQuery.cte` and include it with\n:meth:`~Query.with_cte`:\n\nSimple Example\n^^^^^^^^^^^^^^\n\nFor an example, let's say we have some data points that consist of a key and a\nfloating-point value. Let's define our model and populate some test data:\n\n.. code-block:: python\n\n   class Sample(Model):\n       key = TextField()\n       value = FloatField()\n\n   data = (\n       ('a', (1.25, 1.5, 1.75)),\n       ('b', (2.1, 2.3, 2.5, 2.7, 2.9)),\n       ('c', (3.5, 3.5)))\n\n   # Populate data.\n   for key, values in data:\n       Sample.insert_many([(key, value) for value in values],\n                          fields=[Sample.key, Sample.value]).execute()\n\nLet's use a CTE to calculate, for each distinct key, which values were\nabove-average for that key.\n\n.. code-block:: python\n\n   # First we'll declare the query that will be used as a CTE. This query\n   # simply determines the average value for each key.\n   cte = (Sample\n          .select(Sample.key, fn.AVG(Sample.value).alias('avg_value'))\n          .group_by(Sample.key)\n          .cte('key_avgs', columns=('key', 'avg_value')))\n\n   # Now we'll query the sample table, using our CTE to find rows whose value\n   # exceeds the average for the given key. We'll calculate how far above the\n   # average the given sample's value is, as well.\n   query = (Sample\n            .select(Sample.key, Sample.value)\n            .join(cte, on=(Sample.key == cte.c.key))\n            .where(Sample.value > cte.c.avg_value)\n            .order_by(Sample.value)\n            .with_cte(cte))\n\nWe can iterate over the samples returned by the query to see which samples had\nabove-average values for their given group:\n\n.. code-block:: pycon\n\n   >>> for sample in query:\n   ...     print(sample.key, sample.value)\n\n   # 'a', 1.75\n   # 'b', 2.7\n   # 'b', 2.9\n\nComplex Example\n^^^^^^^^^^^^^^^\n\nFor a more complete example, let's consider the following query which uses\nmultiple CTEs to find per-product sales totals in only the top sales regions.\nOur model looks like this:\n\n.. code-block:: python\n\n   class Order(Model):\n       region = TextField()\n       amount = FloatField()\n       product = TextField()\n       quantity = IntegerField()\n\nHere is how the query might be written in SQL. This example can be found in\nthe `postgresql documentation <https://www.postgresql.org/docs/current/static/queries-with.html>`_.\n\n.. code-block:: sql\n\n   WITH regional_sales AS (\n       SELECT region, SUM(amount) AS total_sales\n       FROM orders\n       GROUP BY region\n     ), top_regions AS (\n       SELECT region\n       FROM regional_sales\n       WHERE total_sales > (SELECT SUM(total_sales) / 10 FROM regional_sales)\n     )\n   SELECT region,\n          product,\n          SUM(quantity) AS product_units,\n          SUM(amount) AS product_sales\n   FROM orders\n   WHERE region IN (SELECT region FROM top_regions)\n   GROUP BY region, product;\n\nWith Peewee, we would write:\n\n.. code-block:: python\n\n   reg_sales = (Order\n                .select(Order.region,\n                        fn.SUM(Order.amount).alias('total_sales'))\n                .group_by(Order.region)\n                .cte('regional_sales'))\n\n   top_regions = (reg_sales\n                  .select(reg_sales.c.region)\n                  .where(reg_sales.c.total_sales > (\n                      reg_sales.select(fn.SUM(reg_sales.c.total_sales) / 10)))\n                  .cte('top_regions'))\n\n   query = (Order\n            .select(Order.region,\n                    Order.product,\n                    fn.SUM(Order.quantity).alias('product_units'),\n                    fn.SUM(Order.amount).alias('product_sales'))\n            .where(Order.region.in_(top_regions.select(top_regions.c.region)))\n            .group_by(Order.region, Order.product)\n            .with_cte(reg_sales, top_regions))\n\nRecursive CTEs\n^^^^^^^^^^^^^^\n\nPeewee supports recursive CTEs. Recursive CTEs can be useful when, for example,\nyou have a tree data-structure represented by a parent-link foreign key.\nSuppose, for example, that we have a hierarchy of categories for an online\nbookstore. We wish to generate a table showing all categories and their\nabsolute depths, along with the path from the root to the category.\n\nWe'll assume the following model definition, in which each category has a\nforeign-key to its immediate parent category:\n\n.. code-block:: python\n\n   class Category(Model):\n       name = TextField()\n       parent = ForeignKeyField('self', backref='children', null=True)\n\nTo list all categories along with their depth and parents, we can use a\nrecursive CTE:\n\n.. code-block:: python\n\n   # Define the base case of our recursive CTE. This will be categories that\n   # have a null parent foreign-key.\n   Base = Category.alias()\n   level = Value(1).alias('level')\n   path = Base.name.alias('path')\n   base_case = (Base\n                .select(Base.id, Base.name, Base.parent, level, path)\n                .where(Base.parent.is_null())\n                .cte('base', recursive=True))\n\n   # Define the recursive terms.\n   RTerm = Category.alias()\n   rlevel = (base_case.c.level + 1).alias('level')\n   rpath = base_case.c.path.concat('->').concat(RTerm.name).alias('path')\n   recursive = (RTerm\n                .select(RTerm.id, RTerm.name, RTerm.parent, rlevel, rpath)\n                .join(base_case, on=(RTerm.parent == base_case.c.id)))\n\n   # The recursive CTE is created by taking the base case and UNION ALL with\n   # the recursive term.\n   cte = base_case.union_all(recursive)\n\n   # We will now query from the CTE to get the categories, their levels,  and\n   # their paths.\n   query = (cte\n            .select_from(cte.c.name, cte.c.level, cte.c.path)\n            .order_by(cte.c.path))\n\n   # We can now iterate over a list of all categories and print their names,\n   # absolute levels, and path from root -> category.\n   for category in query:\n       print(category.name, category.level, category.path)\n\n   # Example output:\n   # root, 1, root\n   # p1, 2, root->p1\n   # c1-1, 3, root->p1->c1-1\n   # c1-2, 3, root->p1->c1-2\n   # p2, 2, root->p2\n   # c2-1, 3, root->p2->c2-1\n\nData-Modifying CTE\n^^^^^^^^^^^^^^^^^^\n\nPeewee supports data-modifying CTE's.\n\nExample of using a data-modifying CTE to move data from one table to an archive\ntable, using a single query:\n\n.. code-block:: python\n\n   class Event(Model):\n       name = CharField()\n       timestamp = DateTimeField()\n\n   class Archive(Model):\n       name = CharField()\n       timestamp = DateTimeField()\n\n   # Move rows older than 24 hours from the Event table to the Archive.\n   cte = (Event\n          .delete()\n          .where(Event.timestamp < (datetime.now() - timedelta(days=1)))\n          .returning(Event)\n          .cte('moved_rows'))\n\n   # Create a simple SELECT to get the resulting rows from the CTE.\n   src = Select((cte,), (cte.c.id, cte.c.name, cte.c.timestamp))\n\n   # Insert into the archive table whatever data was returned by the DELETE.\n   res = (Archive\n          .insert_from(src, (Archive.id, Archive.name, Archive.timestamp))\n          .with_cte(cte)\n          .execute())\n\nThe above corresponds to, roughly, the following SQL:\n\n.. code-block:: sql\n\n   WITH \"moved_rows\" AS (\n       DELETE FROM \"event\" WHERE (\"timestamp\" < XXXX-XX-XXTXX:XX:XX)\n       RETURNING \"id\", \"name\", \"timestamp\")\n   INSERT INTO \"archive\" (\"id\", \"name\", \"timestamp\")\n   SELECT \"moved_rows\".\"id\", \"moved_rows\".\"name\", \"moved_rows\".\"timestamp\"\n   FROM \"moved_rows\";\n\nFor additional examples, refer to the tests in ``models.py`` and ``sql.py``:\n\n* https://github.com/coleifer/peewee/blob/master/tests/models.py\n* https://github.com/coleifer/peewee/blob/master/tests/sql.py\n"
  },
  {
    "path": "docs/peewee/quickstart.rst",
    "content": ".. _quickstart:\n\nQuickstart\n==========\n\nThis guide walks through defining a schema, writing rows, and reading them\nback. It takes about ten minutes. Every concept introduced here is covered in\ndepth in the following documents.\n\n.. tip::\n   Follow along in an interactive Python session.\n\nModel Definition\n-----------------\n\nA Peewee application starts with a :class:`Database` object and one or more\n:class:`Model` classes. The database object manages connections; model\nclasses map to tables.\n\n.. code-block:: python\n\n   import datetime\n   from peewee import *\n\n   # An in-memory SQLite database. Or use PostgresqlDatabase or MySQLDatabase.\n   db = SqliteDatabase(':memory:')\n\n   class BaseModel(Model):\n       \"\"\"All models inherit this to share the database connection.\"\"\"\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = TextField(unique=True)\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(\n           default=datetime.datetime.now,\n           index=True)\n\nThree things to notice:\n\n* ``BaseModel`` exists only to carry the ``database`` setting. Every subclass\n  inherits it automatically.\n* Peewee adds an auto-incrementing integer ``id`` primary key to any model\n  that does not declare its own.\n* ``ForeignKeyField`` links ``Tweet`` to ``User``. The ``backref='tweets'``\n  parameter means every ``User`` instance gains a ``tweets`` attribute.\n\nCreate the Tables\n-----------------\n\n.. code-block:: python\n\n   db.connect()\n   db.create_tables([User, Tweet])\n\n:meth:`~Database.create_tables` generates ``CREATE TABLE`` statements for\neach model. By default ``create_table()`` specifies ``safe=True``, which uses\n``CREATE TABLE IF NOT EXISTS``, making it safe to call on every startup.\n\nWriting Data\n------------\n\nCreate a row with :meth:`~Model.create` (one step) or instantiate a model\nand call :meth:`~Model.save` (two steps):\n\n.. code-block:: python\n\n   # One-step creation - returns the saved instance.\n   charlie = User.create(username='charlie')\n   huey = User.create(username='huey')\n\n   # Two-step creation.\n   t = Tweet(user=charlie, content='Hello, world!')\n   t.save()\n\n   Tweet.create(user=charlie, content='My second tweet.')\n   Tweet.create(user=huey, content='meow')\n\nTo update an existing row, modify attributes and call ``save()`` again:\n\n.. code-block:: python\n\n   charlie.username = 'charlie_admin'\n   charlie.save()\n\nTo delete a row:\n\n.. code-block:: python\n\n   stale_tweet = Tweet.get(Tweet.content == 'My second tweet.')\n   stale_tweet.delete_instance()\n\nReading Data\n------------\n\nRetrieve a single row with :meth:`~Model.get`. It raises :exc:`~Model.DoesNotExist`\nif no match is found:\n\n.. code-block:: python\n\n   user = User.get(User.username == 'charlie_admin')\n   print(user.id, user.username)\n\nRetrieve multiple rows with :meth:`~Model.select`. The result is a lazy\nquery - rows are fetched only when you iterate:\n\n.. code-block:: python\n\n   for tweet in Tweet.select():\n       print(tweet.content)\n\nFilter with :meth:`~Query.where`:\n\n.. code-block:: python\n\n   for tweet in Tweet.select().where(Tweet.user == charlie):\n       print(tweet.content)\n\n   for tweet in Tweet.select().where(Tweet.timestamp.year == 2026):\n       print(tweet.content)\n\nSort with :meth:`~Query.order_by`:\n\n.. code-block:: python\n\n   for tweet in Tweet.select().order_by(Tweet.timestamp.desc()):\n       print(tweet.timestamp, tweet.content)\n\nJoin to combine data from related tables in a single query:\n\n.. code-block:: python\n\n   # Fetch each tweet alongside its author's username.\n   # Without the join, accessing tweet.user.username would issue\n   # an extra query per tweet - see the N+1 section in Relationships.\n   query = (Tweet\n            .select(Tweet, User)\n            .join(User)\n            .order_by(Tweet.timestamp.desc()))\n\n   for tweet in query:\n       print(tweet.user.username, '->', tweet.content)\n\nSimple Aggregates\n-----------------\n\nHow many tweets are in the database:\n\n.. code-block:: python\n\n   Tweet.select().count()\n\nWhen the most-recent tweet was added:\n\n.. code-block:: python\n\n   Tweet.select(fn.MAX(Tweet.timestamp)).scalar()\n\nClose the Connection\n--------------------\n\nWhen done using the database, close the connection:\n\n.. code-block:: python\n\n   db.close()\n\nIn a web application you would open the connection when a request arrives and\nclose it when the response is sent. See :ref:`framework-integration` for\nframework-specific patterns.\n\nWorking with Existing databases\n-------------------------------\n\nIf you have an existing database, peewee can generate models using :ref:`pwiz`.\nFor example to generate models for a Postgres database named ``blog_db``:\n\n.. code-block:: shell\n\n   python -m pwiz -e postgresql blog > blog_models.py\n\nWhat Next\n---------\n\nEach concept introduced above is covered in full detail in the following\ndocuments:\n\n* :ref:`database` - connection options, multiple backends, run-time\n  configuration, connection pooling.\n* :ref:`models` - field types, field parameters, model Meta options, indexes,\n  primary keys.\n* :ref:`relationships` - how foreign keys work at runtime, joins, the N+1\n  problem, many-to-many relationships.\n* :ref:`querying` - the full SELECT API: filtering, sorting, aggregates,\n  window functions, CTEs.\n* :ref:`writing` - INSERT, UPDATE, DELETE, bulk operations, upsert.\n* :ref:`transactions` - atomic blocks, nesting, savepoints.\n\nFor a complete worked example building a small web application, see\n:ref:`example`.\n"
  },
  {
    "path": "docs/peewee/recipes.rst",
    "content": ".. _recipes:\n\nRecipes\n=======\n\nCollected patterns for common real-world problems. Each recipe assumes\nfamiliarity with :ref:`querying`, :ref:`writing`, and :ref:`relationships`.\n\nAll examples use the following models:\n\n.. code-block:: python\n\n   import datetime\n   from peewee import *\n\n   db = SqliteDatabase(':memory:')\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = TextField(unique=True)\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(default=datetime.datetime.now)\n       created_date = DateTimeField(default=datetime.datetime.now)\n\n\n.. _optimistic-locking:\n\nOptimistic Locking\n------------------\n\n*Optimistic locking* avoids holding a database lock across the read-modify-write\ncycle by recording a version number on each row. On write, the update is\nconditional on the version not having changed. If another process modified the\nrow in the meantime, the update matches zero rows and the conflict is detected\nin application code.\n\nThis is a lighter-weight alternative to ``SELECT FOR UPDATE`` (Postgresql) or\n``BEGIN IMMEDIATE`` (SQLite) when lock contention is expected to be low.\n\nA reusable base class:\n\n.. code-block:: python\n\n   class ConflictDetectedException(Exception):\n       pass\n\n   class BaseVersionedModel(BaseModel):\n       version = IntegerField(default=1, index=True)\n\n       def save_optimistic(self):\n           if not self.id:\n               # This is a new record, so the default logic is to perform an\n               # INSERT. Ideally your model would also have a unique\n               # constraint that made it impossible for two INSERTs to happen\n               # at the same time.\n               return self.save()\n\n           # Update any data that has changed and bump the version counter.\n           field_data = dict(self.__data__)\n           current_version = field_data.pop('version', 1)\n           self._populate_unsaved_relations(field_data)\n           field_data = self._prune_fields(field_data, self.dirty_fields)\n           if not field_data:\n               raise ValueError('No changes have been made.')\n\n           ModelClass = type(self)\n           field_data['version'] = ModelClass.version + 1  # Atomic increment.\n\n           updated = (ModelClass\n                      .update(**field_data)\n                      .where(\n                          (ModelClass.version == current_version) &\n                          (ModelClass.id == self.id))\n                      .execute())\n           if updated == 0:\n               # No rows were updated, indicating another process has saved\n               # a new version.\n               raise ConflictDetectedException()\n           else:\n               # Increment local version to match what is now in the db.\n               self.version += 1\n               return True\n\nUsage:\n\n.. code-block:: pycon\n\n   class UserProfile(BaseVersionedModel):\n       username = TextField(unique=True)\n       bio = TextField(default='')\n\n   >>> u = UserProfile(username='charlie')\n   >>> u.save_optimistic()\n   True\n\n   >>> u.bio = 'Python developer'\n   >>> u.save_optimistic()\n   True\n   >>> u.version\n   2\n\n   # Simulate a concurrent modification:\n   >>> u2 = UserProfile.get(UserProfile.username == 'charlie')\n   >>> u2.bio = 'Changed by another process'\n   >>> u2.save_optimistic()\n   True\n\n   # The original instance's version is now stale:\n   >>> u.bio = 'My update'\n   >>> u.save_optimistic()\n   ConflictDetectedException\n\n\nGet-or-Create Safely\n---------------------\n\n:meth:`~Model.get_or_create` is convenient but has a small race window\nbetween the SELECT and the INSERT when the row does not yet exist. Two\nconcurrent processes can both fail the SELECT and both attempt the INSERT,\ncausing one to fail with an ``IntegrityError``.\n\nThe safe pattern attempts the INSERT first and falls back to a GET on\n``IntegrityError``:\n\n.. code-block:: python\n\n   def get_or_create_user(username):\n       try:\n           with db.atomic():\n               return User.create(username=username), True\n       except IntegrityError:\n           return User.get(User.username == username), False\n\n   user, created = get_or_create_user('charlie')\n\nThe ``db.atomic()`` wrapper is important: it ensures that the rollback on\n``IntegrityError`` affects only this operation, not any surrounding transaction.\n\n\n.. _top-item-per-group:\n\nTop Item Per Group\n------------------\n\nThese examples find the single most recent tweet for each user. See\n:ref:`top-n-per-group` below for the generalized N-per-group problem.\n\nThe most portable approach uses a ``MAX()`` aggregate in a non-correlated\nsubquery, then joins back to the tweet table on both user and timestamp:\n\n.. code-block:: python\n\n   # When referencing a table multiple times, we'll call Model.alias() to create\n   # a secondary reference to the table.\n   TweetAlias = Tweet.alias()\n\n   # Create a subquery that will calculate the maximum Tweet created_date for\n   # each user.\n   subquery = (TweetAlias\n               .select(\n                   TweetAlias.user,\n                   fn.MAX(TweetAlias.created_date).alias('max_ts'))\n               .group_by(TweetAlias.user)\n               .alias('tweet_max'))\n\n   # Query for tweets and join using the subquery to match the tweet's user\n   # and created_date.\n   query = (Tweet\n            .select(Tweet, User)\n            .join(User)\n            .switch(Tweet)\n            .join(subquery, on=(\n                (Tweet.created_date == subquery.c.max_ts) &\n                (Tweet.user == subquery.c.user_id))))\n\nSQLite and MySQL permit a shorter form that groups by a subset of selected\ncolumns:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select(Tweet, User)\n            .join(User)\n            .group_by(Tweet.user)\n            .having(Tweet.created_date == fn.MAX(Tweet.created_date)))\n\nPostgresql requires the standard subquery form above.\n\n.. _top-n-per-group:\n\nTop N Per Group\n---------------\n\nThese examples describe several ways to query the top *N* items per group\nreasonably efficiently. For a thorough discussion of various techniques, check\nout my blog post `Querying the top N objects per group with Peewee ORM\n<https://charlesleifer.com/blog/querying-the-top-n-objects-per-group-with-peewee-orm/>`_.\n\nWindow functions\n^^^^^^^^^^^^^^^^\n\nA ``RANK()`` window function is the cleanest solution. Rank tweets per user by\ntimestamp (newest first), then filter the outer query to the top N ranks:\n\n.. code-block:: python\n\n   TweetAlias = Tweet.alias()\n\n   ranked = (TweetAlias\n             .select(\n                 TweetAlias.content,\n                 User.username,\n                 fn.RANK().over(\n                     partition_by=[TweetAlias.user],\n                     order_by=[TweetAlias.created_date.desc()]\n                 ).alias('rnk'))\n             .join(User, on=(TweetAlias.user == User.id))\n             .alias('subq'))\n\n   query = (Tweet\n            .select(ranked.c.content, ranked.c.username)\n            .from_(ranked)\n            .where(ranked.c.rnk <= 3))\n\nPostgresql - lateral joins\n^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nA ``LATERAL`` join executes a correlated subquery once per row of the driving\ntable. For each user, it selects the three most recent tweets.\n\nThe desired SQL is:\n\n.. code-block:: sql\n\n    SELECT * FROM\n      (SELECT id, username FROM user) AS uq\n       LEFT JOIN LATERAL\n      (SELECT message, created_date\n       FROM tweet\n       WHERE (user_id = uq.id)\n       ORDER BY created_date DESC LIMIT 3)\n      AS pq ON true\n\nTo accomplish this with peewee is quite straightforward:\n\n.. code-block:: python\n\n    subq = (Tweet\n            .select(Tweet.message, Tweet.created_date)\n            .where(Tweet.user == User.id)\n            .order_by(Tweet.created_date.desc())\n            .limit(3))\n\n    query = (User\n             .select(User, subq.c.content, subq.c.created_date)\n             .join(subq, JOIN.LEFT_LATERAL)\n             .order_by(User.username, subq.c.created_date.desc()))\n\n    # We queried from the \"perspective\" of user, so the rows are User instances\n    # with the addition of a \"content\" and \"created_date\" attribute for each of\n    # the (up-to) 3 most-recent tweets for each user.\n    for row in query:\n        print(row.username, row.content, row.created_date)\n\nTo implement an equivalent query from the \"perspective\" of the Tweet model, we\ncan instead write:\n\n.. code-block:: python\n\n    # subq is the same as the above example.\n    subq = (Tweet\n            .select(Tweet.message, Tweet.created_date)\n            .where(Tweet.user == User.id)\n            .order_by(Tweet.created_date.desc())\n            .limit(3))\n\n    query = (Tweet\n             .select(User.username, subq.c.content, subq.c.created_date)\n             .from_(User)\n             .join(subq, JOIN.LEFT_LATERAL)\n             .order_by(User.username, subq.c.created_date.desc()))\n\n    # Each row is a \"tweet\" instance with an additional \"username\" attribute.\n    # This will print the (up-to) 3 most-recent tweets from each user.\n    for tweet in query:\n        print(tweet.username, tweet.content, tweet.created_date)\n\nCorrelated subquery count\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nA correlated subquery that counts tweets newer than the current row can also be\nused. Rows where fewer than N newer tweets exist are in the top N:\n\n.. code-block:: python\n\n   TweetAlias = Tweet.alias()\n\n   # Create a correlated subquery that calculates the number of\n   # tweets with a higher (newer) timestamp than the tweet we're\n   # looking at in the outer query.\n   subquery = (TweetAlias\n               .select(fn.COUNT(TweetAlias.id))\n               .where(\n                   (TweetAlias.created_date >= Tweet.created_date) &\n                   (TweetAlias.user == Tweet.user)))\n\n   # Wrap the subquery and filter on the count.\n   query = (Tweet\n            .select(Tweet, User)\n            .join(User)\n            .where(subquery <= 3))\n\nSQLite and MySQL - self-join\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nAn alternative: self-join and count newer tweets in the HAVING clause:\n\n.. code-block:: python\n\n   TweetAlias = Tweet.alias()\n\n   query = (Tweet\n            .select(Tweet.id, Tweet.content, Tweet.user, User.username)\n            .join_from(Tweet, User)\n            .join_from(Tweet, TweetAlias, on=(\n                (TweetAlias.user == Tweet.user) &\n                (TweetAlias.created_date >= Tweet.created_date)))\n            .group_by(Tweet.id, Tweet.content, Tweet.user, User.username)\n            .having(fn.COUNT(Tweet.id) <= 3))\n\nThe last example uses a ``LIMIT`` clause in a correlated subquery.\n\n.. code-block:: python\n\n    TweetAlias = Tweet.alias()\n\n    # The subquery here will calculate, for the user who created the\n    # tweet in the outer loop, the three newest tweets. The expression\n    # will evaluate to `True` if the outer-loop tweet is in the set of\n    # tweets represented by the inner query.\n    query = (Tweet\n             .select(Tweet, User)\n             .join(User)\n             .where(Tweet.id << (\n                 TweetAlias\n                 .select(TweetAlias.id)\n                 .where(TweetAlias.user == Tweet.user)\n                 .order_by(TweetAlias.created_date.desc())\n                 .limit(3))))\n\nFor a thorough benchmark comparison of these approaches, see the blog post\n`Querying the top N objects per group with Peewee ORM\n<https://charlesleifer.com/blog/querying-the-top-n-objects-per-group-with-peewee-orm/>`_.\n\n\nBulk-Loading with Explicit Primary Keys\n-----------------------------------------\n\nWhen loading relational data from an external source where primary keys are\nalready assigned, use :meth:`~Model.insert_many` with the ``id`` field\nincluded. This avoids the ``auto_increment`` workaround that was common in\nolder Peewee versions:\n\n.. code-block:: python\n\n   data = [(1, 'alice'), (2, 'bob'), (3, 'carol')]\n   fields = [User.id, User.username]\n\n   with db.atomic():\n       User.insert_many(data, fields=fields).execute()\n\nBecause ``insert_many`` never reads rows back, there is no confusion between\nINSERT and UPDATE paths.\n\n\nCustom SQLite Functions\n-----------------------\n\nSQLite can be extended with Python functions that are callable from SQL. This\nis useful for operations SQLite does not natively support.\n\nRegistering a function with the ``@db.func()`` decorator makes it available\nimmediately after the connection is opened:\n\n.. code-block:: python\n\n   from hashlib import sha256\n   import os\n\n   db = SqliteDatabase('my_app.db')\n\n   def _hash_password(salt, password):\n       return sha256((salt + password).encode()).hexdigest()\n\n   @db.func()\n   def make_password(raw_password):\n       salt = os.urandom(8).hex()\n       return salt + '$' + _hash_password(salt, raw_password)\n\n   @db.func()\n   def check_password(raw_password, stored):\n       salt, hsh = stored.split('$', 1)\n       return hsh == _hash_password(salt, raw_password)\n\nStore a hashed password:\n\n.. code-block:: python\n\n   User.insert(username='charlie',\n               password=fn.make_password('s3cr3t')).execute()\n\nVerify a password at login:\n\n.. code-block:: python\n\n   def login(username, raw_password):\n       try:\n           return (User\n                   .select()\n                   .where(\n                       (User.username == username) &\n                       (fn.check_password(raw_password, User.password) == True))\n                   .get())\n       except User.DoesNotExist:\n           return None\n\n.. seealso::\n   :meth:`SqliteDatabase.func`,\n   :meth:`SqliteDatabase.aggregate`,\n   :meth:`SqliteDatabase.window_function`.\n\n\nDate Arithmetic Across Databases\n----------------------------------\n\nEach database implements date arithmetic differently. This section shows how to\nexpress \"next occurrence of a scheduled task\" - defined as\n``last_run + interval_seconds`` - for each backend.\n\nThe schema:\n\n.. code-block:: python\n\n   class Schedule(BaseModel):\n       interval = IntegerField()   # Repeat every N seconds.\n\n   class Task(BaseModel):\n       schedule = ForeignKeyField(Schedule, backref='tasks')\n       command  = TextField()\n       last_run = DateTimeField()\n\nWe want: tasks where ``now >= last_run + interval``.\n\nOur desired code would look like:\n\n.. code-block:: python\n\n    next_occurrence = something  # ??? how do we define this ???\n\n    # We can express the current time as a Python datetime value, or we could\n    # alternatively use the appropriate SQL function/name.\n    now = Value(datetime.datetime.now())  # Or SQL('current_timestamp'), e.g.\n\n    query = (Task\n             .select(Task, Schedule)\n             .join(Schedule)\n             .where(now >= next_occurrence))\n\n**Postgresql** - multiply a typed interval:\n\n.. code-block:: python\n\n   one_second = SQL(\"INTERVAL '1 second'\")\n   next_run = Task.last_run + (Schedule.interval * one_second)\n\n   now = Value(datetime.datetime.now())\n   tasks_due = (Task\n                .select(Task, Schedule)\n                .join(Schedule)\n                .where(now >= next_run))\n\n**MySQL** - use ``DATE_ADD`` with a dynamic INTERVAL expression:\n\n.. code-block:: python\n\n   from peewee import NodeList\n\n   interval = NodeList((SQL('INTERVAL'), Schedule.interval, SQL('SECOND')))\n   next_run = fn.DATE_ADD(Task.last_run, interval)\n\n   now = Value(datetime.datetime.now())\n   tasks_due = (Task\n                .select(Task, Schedule)\n                .join(Schedule)\n                .where(now >= next_run))\n\n**SQLite** - convert to Unix timestamp, add seconds, convert back:\n\n.. code-block:: python\n\n   next_ts = fn.strftime('%s', Task.last_run) + Schedule.interval\n   next_run = fn.datetime(next_ts, 'unixepoch')\n\n   now = Value(datetime.datetime.now())\n   tasks_due = (Task\n                .select(Task, Schedule)\n                .join(Schedule)\n                .where(now >= next_run))\n"
  },
  {
    "path": "docs/peewee/relationships.rst",
    "content": ".. _relationships:\n\nRelationships and Joins\n=======================\n\nRelational databases derive most of their power from the ability to link rows\nacross tables. This document explains how Peewee models those links, what\nhappens under the hood when you traverse them, and how to write queries that\ncross table boundaries efficiently.\n\nBy the end of this document you will understand:\n\n* How :class:`ForeignKeyField` behaves at runtime, not just at schema\n  definition time.\n* What a back-reference is and when to use one.\n* What the N+1 problem is and how to recognise it.\n* How to write joins, including multi-table and self-referential joins.\n* How many-to-many relationships are modelled.\n* When to use :func:`prefetch` instead of a join.\n\n\nModel Definitions\n-----------------\n\nAll examples in this document use the following three models. They will be\ndefined once here and reused throughout.\n\n.. code-block:: python\n\n   import datetime\n   from peewee import *\n\n   db = SqliteDatabase(':memory:')\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = TextField()\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(default=datetime.datetime.now)\n\n   class Favorite(BaseModel):\n       user = ForeignKeyField(User, backref='favorites')\n       tweet = ForeignKeyField(Tweet, backref='favorites')\n\nA :class:`ForeignKeyField` links one model to another. ``Tweet.user`` links\neach tweet to the user who wrote it. ``Favorite.user`` and ``Favorite.tweet``\ntogether record which users have favorited which tweets.\n\nThe following helper populates test data that the examples below will query:\n\n.. code-block:: python\n\n   def create_test_data():\n       db.create_tables([User, Tweet, Favorite])\n\n       users = {\n           name: User.create(username=name)\n           for name in ('huey', 'mickey', 'zaizee')\n       }\n\n       tweet_data = {\n           'huey':   ('meow', 'hiss', 'purr'),\n           'mickey': ('woof', 'whine'),\n           'zaizee': (),\n       }\n       tweets = {}\n       for username, contents in tweet_data.items():\n           for content in contents:\n               tweets[content] = Tweet.create(\n                   user=users[username],\n                   content=content)\n\n       # huey favorites mickey's \"whine\",\n       # mickey favorites huey's \"purr\",\n       # zaizee favorites huey's \"meow\" and \"purr\".\n       favorite_data = (\n           ('huey',   ['whine']),\n           ('mickey', ['purr']),\n           ('zaizee', ['meow', 'purr']),\n       )\n       for username, contents in favorite_data:\n           for content in contents:\n               Favorite.create(user=users[username], tweet=tweets[content])\n\nThis gives the following data:\n\n========= ============= ==================\nUser      Tweet         Favorited by\n========= ============= ==================\nhuey      meow          zaizee\nhuey      hiss\nhuey      purr          mickey, zaizee\nmickey    woof\nmickey    whine         huey\nzaizee    (no tweets)\n========= ============= ==================\n\n.. note::\n   To log every query Peewee executes to the console - useful for verifying\n   query counts while working through this document - add the following before\n   running any queries:\n\n   .. code-block:: python\n\n      import logging\n      logging.getLogger('peewee').addHandler(logging.StreamHandler())\n      logging.getLogger('peewee').setLevel(logging.DEBUG)\n\n.. _foreign-keys:\n\nForeign Keys\n------------\n\nWhen you declare a :class:`ForeignKeyField`, Peewee creates two things on\nthe model: a field that stores the raw integer ID value, and a descriptor that\nresolves that ID into a full model instance on access.\n\n.. code-block:: python\n\n   tweet = Tweet.get(Tweet.content == 'meow')\n\n   # Accessing .user resolves the foreign key - Peewee issues a SELECT\n   # query to fetch the related User row.\n   print(tweet.user.username)  # 'huey'\n\n   # Accessing .user_id returns the raw integer stored in the column,\n   # without issuing any query.\n   print(tweet.user_id)  # 1\n\nThe ``_id`` suffix accessor is available for every foreign key field. Use it\nwhenever only the ID value is needed, since it avoids the extra query entirely.\n\nLazy loading\n^^^^^^^^^^^^\n\nBy default, a :class:`ForeignKeyField` is *lazy-loaded*: the related object\nis not fetched until the attribute is first accessed, at which point a\n``SELECT`` query is issued automatically. This is convenient but can lead to\nperformance problems - see :ref:`nplusone` below.\n\nTo disable lazy loading on a specific field, pass ``lazy_load=False``. With\nlazy loading disabled, accessing the attribute returns the raw ID value rather\nthan issuing a query, matching the behaviour of the ``_id`` accessor:\n\n.. code-block:: python\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets', lazy_load=False)\n\n   for tweet in Tweet.select():\n       # Returns the integer ID, not a User instance. No extra query.\n       print(tweet.user)\n\n   # If the User data was eagerly loaded via a join, the full User\n   # instance is accessible as normal, even with lazy_load=False.\n   for tweet in Tweet.select(Tweet, User).join(User):\n       print(tweet.user.username)\n\n.. seealso::\n   :ref:`nplusone` explains when and why disabling lazy loading is useful.\n\n.. _backreferences:\n\nBack-references\n---------------\n\nEvery :class:`ForeignKeyField` automatically creates a *back-reference* on\nthe related model. The back-reference is a pre-filtered :class:`Select`\nquery that returns all rows pointing at a given instance.\n\nIn the example schema, ``Tweet.user`` is a foreign key to ``User``. The\n``backref='tweets'`` parameter means that every ``User`` instance gains a\n``tweets`` attribute, which is a pre-filtered :class:`Select` query:\n\n.. code-block:: pycon\n\n   >>> huey = User.get(User.username == 'huey')\n\n   >>> huey.tweets  # back-reference is a Select query.\n   <peewee.ModelSelect object at 0x...>\n\n   >>> for tweet in huey.tweets:\n   ...     print(tweet.content)\n   meow\n   hiss\n   purr\n\nTaking a closer look at ``huey.tweets``, we can see that it is just a simple\npre-filtered ``SELECT`` query:\n\n.. code-block:: pycon\n\n   >>> huey.tweets\n   <peewee.ModelSelect at 0x7f0483931fd0>\n\n   >>> huey.tweets.sql()\n   ('SELECT \"t1\".\"id\", \"t1\".\"content\", \"t1\".\"timestamp\", \"t1\".\"user_id\"\n     FROM \"tweet\" AS \"t1\" WHERE (\"t1\".\"user_id\" = ?)', [1])\n\nA back-reference behaves like any other :class:`Select` query and can be\nfiltered, ordered, and chained:\n\n.. code-block:: python\n\n   recent = (huey.tweets\n             .order_by(Tweet.timestamp.desc())\n             .limit(2))\n\nIf no ``backref`` name is specified, Peewee generates one automatically using\nthe pattern ``<lowercase_classname>_set``. Specifying an explicit ``backref``\nis recommended for clarity.\n\n.. _nplusone:\n\nThe N+1 Problem\n---------------\n\nThe *N+1 problem* occurs when code issues one query to fetch a list of N rows,\nthen issues one or more additional queries *per row* to fetch related data -\nN+1 queries in total instead of one or two. At small scale this is invisible,\nbut at production scale it can make pages that should take milliseconds take\nseconds.\n\nConsider printing every tweet alongside its author's username:\n\n.. code-block:: python\n\n   # Bad: issues 1 query for tweets + 1 query per tweet for the user.\n   for tweet in Tweet.select():\n       print(tweet.user.username, '->', tweet.content)\n\n   # Good: only one query is needed.\n   query = (Tweet\n            .select(Tweet, User)\n            .join(User))\n\n   for tweet in query:\n       # tweet.user is a User instance populated from the joined data.\n       # No additional query is issued.\n       print(tweet.user.username, '->', tweet.content)\n\nWithout joining and selecting the related User, each access to ``tweet.user``\ntriggers a ``SELECT`` on the ``user`` table. With five tweets, this produces\nsix queries. With five thousand tweets, it produces five thousand and one.\n\nThe same problem can occur when iterating over back-references:\n\n.. code-block:: python\n\n   # Bad: issues 1 query for users + 1 query per user for their tweets.\n   for user in User.select():\n       print(user.username)\n       for tweet in user.tweets:  # A new query for each user.\n           print('  ', tweet.content)\n\n   # Better:\n   for user in User.select().prefetch(Tweet):\n       print(user.username)\n       for tweet in user.tweets:  # Pre-fetched, no additional query.\n           print('  ', tweet.content)\n\nPeewee provides two complementary tools for avoiding N+1 queries:\n\n* **Joins** - combine rows from multiple tables in a single ``SELECT``. Best\n  when traversing a foreign key *toward* its target (many-to-one direction),\n  for example fetching tweets with their authors.\n* **Prefetch** - issue one query per table and stitch the results together in\n  Python. Best when traversing a back-reference (one-to-many direction), for\n  example fetching users with all their tweets.\n\nBoth are covered in the sections below. The choice between them depends on the\nshape of the query.\n\n.. _joins:\n\nJoins\n-----\n\nA SQL join combines columns from two or more tables into a single result set.\nPeewee's :meth:`~ModelSelect.join` method generates the appropriate ``JOIN``\nclause and, when the full result is returned as model instances, reconstructs\nthe model graph automatically.\n\nJoin context\n^^^^^^^^^^^^\n\nPeewee tracks a *join context*: the model from which the next ``join()`` call\nwill depart. At the start of a query the join context is the model being\nselected from. Each call to ``join()`` moves the join context to the model just\njoined.\n\n.. code-block:: python\n\n   # Context starts at Tweet.\n   # After .join(User), context moves to User.\n   query = Tweet.select().join(User)\n\nWhen joining through multiple tables in a chain, this is usually what you want.\nWhen joining from one model to two different models, the join context needs to\nbe reset explicitly using :meth:`~ModelSelect.switch` or\n:meth:`~ModelSelect.join_from`.\n\nPeewee infers the join predicate (the ``ON`` clause) from the foreign keys\ndefined on the models. If only one foreign key exists between two models, no\nadditional specification is required. If multiple foreign keys exist, the\nrelevant one must be specified explicitly.\n\nThe following code is equivalent to the prevoius example:\n\n.. code-block:: python\n   :emphasize-lines: 3\n\n   query = (Tweet\n            .select()\n            .join(User, on=(Tweet.user == User.id))\n            .where(User.username == 'huey'))\n\nSimple joins\n^^^^^^^^^^^^\n\nTo fetch all of huey's tweets, join from ``Tweet`` to ``User`` and filter on\nthe username:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select()\n            .join(User)\n            .where(User.username == 'huey'))\n\n   for tweet in query:\n       print(tweet.content)\n\nPeewee inferred the join predicate since ``Tweet.user`` is the only key between\nthe two models. To explicitly specify the join predicate use ``on=``:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select()\n            .join(User, on=(Tweet.user == User.id))\n            .where(User.username == 'huey'))\n\nIf a ``User`` instance is already available, the back-reference is simpler and\nequivalent for straightforward cases:\n\n.. code-block:: python\n\n   huey = User.get(User.username == 'huey')\n   for tweet in huey.tweets:\n       print(tweet.content)\n\nThe join is the better choice when filtering or joining further. The\nback-reference is more readable for simple access to related rows.\n\nJoining across multiple tables\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nTo count how many favorites each user has received across all their tweets,\na join must traverse ``User -> Tweet -> Favorite``. Because each join moves the\ncontext forward, this chain can be written directly:\n\n.. code-block:: python\n\n   # Context: User -> join -> Tweet -> join -> Favorite\n   query = (User\n            .select(User.username, fn.COUNT(Favorite.id).alias('fav_count'))\n            .join(Tweet, JOIN.LEFT_OUTER)\n            .join(Favorite, JOIN.LEFT_OUTER)\n            .group_by(User.username))\n\n   for user in query:\n       print(f'{user.username}: {user.fav_count} favorites received')\n\nBoth joins use ``LEFT OUTER`` because a user may have no tweets, and a tweet\nmay have no favorites - yet both should appear in the result with a count of\nzero.\n\nSwitching join context\n^^^^^^^^^^^^^^^^^^^^^^\n\nWhen a query needs to branch - joining from one model to two different models\n- the join context must be reset manually using :meth:`~ModelSelect.switch`.\n\nTo find all tweets by huey and how many times each has been favorited:\n\n.. code-block:: python\n\n   # Context: Tweet -> join -> User (context is now User)\n   # switch(Tweet) resets context to Tweet\n   # -> join -> Favorite (context is now Favorite)\n   query = (Tweet\n            .select(Tweet.content, fn.COUNT(Favorite.id).alias('fav_count'))\n            .join(User)\n            .switch(Tweet)\n            .join(Favorite, JOIN.LEFT_OUTER)\n            .where(User.username == 'huey')\n            .group_by(Tweet.content))\n\n   for tweet in query:\n       print(f'{tweet.content}: favorited {tweet.fav_count} times')\n\nWithout the call to ``.switch(Tweet)``, Peewee would attempt to join from\n``User`` to ``Favorite`` using ``Favorite.user``, which would produce\nincorrect results.\n\nUsing ``join_from``\n^^^^^^^^^^^^^^^^^^^\n\n:meth:`~ModelSelect.join_from` is an alternative to ``switch().join()`` that\nmakes the join source explicit in a single call. The above query can be\nwritten equivalently as:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select(Tweet.content, fn.COUNT(Favorite.id).alias('fav_count'))\n            .join_from(Tweet, User)\n            .join_from(Tweet, Favorite, JOIN.LEFT_OUTER)\n            .where(User.username == 'huey')\n            .group_by(Tweet.content))\n\n``join_from(A, B)`` is equivalent to ``switch(A).join(B)`` and is often more\nreadable when a query branches across several paths.\n\nSelecting columns from joined models\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen columns from multiple models are included in ``select()``, Peewee\nreconstructs the model graph and assigns related model instances to their\ncorresponding attributes.\n\n.. code-block:: python\n\n   query = (Tweet\n            .select(Tweet.content, User.username)\n            .join(User))\n\n   for tweet in query:\n       # tweet.user is a User instance populated from the joined data.\n       # No additional query is issued.\n       print(tweet.user.username, '->', tweet.content)\n\n   # huey -> meow\n   # huey -> hiss\n   # huey -> purr\n   # mickey -> woof\n   # mickey -> whine\n\nTo make it a bit more obvious that it's doing the correct thing, we can ask\nPeewee to return the rows as dictionaries.\n\n.. code-block:: python\n\n   query = (Tweet\n            .select(Tweet.content, User.username)\n            .join(User)\n            .dicts())\n\n   for row in query:\n       print(row)\n\n   # {'content': 'meow', 'username': 'huey'}\n   # {'content': 'hiss', 'username': 'huey'}\n   # {'content': 'purr', 'username': 'huey'}\n   # {'content': 'woof', 'username': 'mickey'}\n   # {'content': 'whine', 'username': 'mickey'}\n\nCompare these queries to the N+1 version: here, only one query is executed\nregardless of how many tweets are returned.\n\nThe attribute name that Peewee uses to store the joined instance follows the\nforeign key field name (``tweet.user`` in this case). To override it, pass\n``attr`` to ``join()``:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select(Tweet.content, User.username)\n            .join(User, attr='author'))\n\n   for tweet in query:\n       print(tweet.author.username, '->', tweet.content)\n\nTo flatten all selected columns onto the primary model instance rather than\nnesting them in a sub-object, append ``.objects()``:\n\n.. code-block:: python\n\n   query = (Tweet\n            .select(Tweet.content, User.username)\n            .join(User)\n            .objects())\n\n   for tweet in query:\n       # username is now an attribute on tweet directly.\n       print(tweet.username, '->', tweet.content)\n\n   # huey -> meow\n\nSee :ref:`row-types` for the different ways Peewee can return rows.\n\nMore complex example\n^^^^^^^^^^^^^^^^^^^^\n\nAs a more complex example, in this query, we will write a single query that\nselects all the favorites, along with the user who created the favorite, the\ntweet that was favorited, and that tweet's author.\n\nIn SQL we would write:\n\n.. code-block:: sql\n\n   SELECT owner.username, tweet.content, author.username AS author\n   FROM favorite\n   INNER JOIN user AS owner ON (favorite.user_id = owner.id)\n   INNER JOIN tweet ON (favorite.tweet_id = tweet.id)\n   INNER JOIN user AS author ON (tweet.user_id = author.id);\n\nNote that we are selecting from the user table twice - once in the context of\nthe user who created the favorite, and again as the author of the tweet.\n\nWith Peewee, we use :meth:`Model.alias` to alias a model class so it can be\nreferenced twice in a single query:\n\n.. code-block:: python\n\n   Owner = User.alias()\n   query = (Favorite\n            .select(Favorite, Tweet.content, User.username, Owner.username)\n            .join_from(Favorite, Owner)  # Determine owner of favorite.\n            .join_from(Favorite, Tweet)  # Join favorite -> tweet.\n            .join_from(Tweet, User))     # Join tweet -> user.\n\nWe can iterate over the results and access the joined values in the following\nway. Note how Peewee has resolved the fields from the various models we\nselected and reconstructed the model graph:\n\n.. code-block:: python\n\n   for fav in query:\n       print(fav.user.username, 'liked', fav.tweet.content, 'by', fav.tweet.user.username)\n\n   # huey liked whine by mickey\n   # mickey liked purr by huey\n   # zaizee liked meow by huey\n   # zaizee liked purr by huey\n\n.. _join-subquery:\n\nSubqueries\n^^^^^^^^^^\n\nPeewee allows you to join on any table-like object, including subqueries or\ncommon table expressions (see :ref:`cte`). To demonstrate joining on a\nsubquery, let's query for all users and their latest tweet.\n\nHere is the SQL:\n\n.. code-block:: sql\n\n   SELECT tweet.*, user.*\n   FROM tweet\n   INNER JOIN (\n       SELECT latest.user_id, MAX(latest.timestamp) AS max_ts\n       FROM tweet AS latest\n       GROUP BY latest.user_id) AS latest_query\n   ON ((tweet.user_id = latest_query.user_id) AND (tweet.timestamp = latest_query.max_ts))\n   INNER JOIN user ON (tweet.user_id = user.id)\n\nWe'll do this by creating a subquery which selects each user and the timestamp\nof their latest tweet. Then we can query the tweets table in the outer query\nand join on the user and timestamp combination from the subquery.\n\n.. code-block:: python\n\n   # Define our subquery first. We'll use an alias of the Tweet model, since\n   # we will be querying from the Tweet model directly in the outer query.\n   Latest = Tweet.alias()\n   latest_query = (Latest\n                   .select(Latest.user, fn.MAX(Latest.timestamp).alias('max_ts'))\n                   .group_by(Latest.user)\n                   .alias('latest_query'))\n\n   # Our join predicate will ensure that we match tweets based on their\n   # timestamp *and* user_id.\n   predicate = ((Tweet.user == latest_query.c.user_id) &\n                (Tweet.timestamp == latest_query.c.max_ts))\n\n   # We put it all together, querying from tweet and joining on the subquery\n   # using the above predicate.\n   query = (Tweet\n            .select(Tweet, User)  # Select all columns from tweet and user.\n            .join_from(Tweet, latest_query, on=predicate)  # Join tweet -> subquery.\n            .join_from(Tweet, User))  # Join from tweet -> user.\n\nIterating over the query, we can see each user and their latest tweet.\n\n.. code-block:: python\n\n   for tweet in query:\n       print(tweet.user.username, '->', tweet.content)\n\n   # huey -> purr\n   # mickey -> whine\n\nThere are a couple things you may not have seen before in the code we used to\ncreate the query in this section:\n\n* We used :meth:`~ModelSelect.join_from` to explicitly specify the join\n  context. We wrote ``.join_from(Tweet, User)``, which is equivalent to\n  ``.switch(Tweet).join(User)``.\n* We referenced columns in the subquery using the magic ``.c`` attribute,\n  for example ``latest_query.c.max_ts``. The ``.c`` attribute is used to\n  dynamically create column references.\n* Instead of passing individual fields to ``Tweet.select()``, we passed the\n  ``Tweet`` and ``User`` models. This is shorthand for selecting all fields on\n  the given model.\n\nCommon-table Expressions\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn the previous section we joined on a subquery, but we could just as easily\nhave used a :ref:`common-table expression (CTE) <cte>`. We will repeat the same\nquery as before, listing users and their latest tweets, but this time we will\ndo it using a CTE.\n\nHere is the SQL:\n\n.. code-block:: sql\n\n   WITH latest AS (\n       SELECT user_id, MAX(timestamp) AS max_ts\n       FROM tweet\n       GROUP BY user_id)\n   SELECT tweet.*, user.*\n   FROM tweet\n   INNER JOIN latest\n       ON ((latest.user_id = tweet.user_id) AND (latest.max_ts = tweet.timestamp))\n   INNER JOIN user\n       ON (tweet.user_id = user.id)\n\nThis example looks very similar to the previous example with the subquery:\n\n.. code-block:: python\n\n   # Define our CTE first. We'll use an alias of the Tweet model, since\n   # we will be querying from the Tweet model directly in the main query.\n   Latest = Tweet.alias()\n   cte = (Latest\n          .select(Latest.user, fn.MAX(Latest.timestamp).alias('max_ts'))\n          .group_by(Latest.user)\n          .cte('latest'))\n\n   # Our join predicate will ensure that we match tweets based on their\n   # timestamp *and* user_id.\n   predicate = ((Tweet.user == cte.c.user_id) &\n                (Tweet.timestamp == cte.c.max_ts))\n\n   # We put it all together, querying from tweet and joining on the CTE\n   # using the above predicate.\n   query = (Tweet\n            .select(Tweet, User)  # Select all columns from tweet and user.\n            .join(cte, on=predicate)  # Join tweet -> CTE.\n            .join_from(Tweet, User)  # Join from tweet -> user.\n            .with_cte(cte))\n\nWe can iterate over the result-set, which consists of the latest tweets for\neach user:\n\n.. code-block:: python\n\n   for tweet in query:\n       print(tweet.user.username, '->', tweet.content)\n\n   # huey -> purr\n   # mickey -> whine\n\n.. note::\n   For more information about using CTEs, including information on writing\n   recursive CTEs, see the :ref:`cte` section of the \"Querying\" document.\n\nMultiple foreign keys to the same model\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen two foreign keys on the same model both point at the same target model,\nPeewee cannot infer which one to use for a join. The field must be specified\nexplicitly.\n\nConsider a ``Relationship`` model recording which users follow which other\nusers:\n\n.. code-block:: python\n\n   class Relationship(BaseModel):\n       from_user = ForeignKeyField(User, backref='following')\n       to_user = ForeignKeyField(User, backref='followers')\n\n       class Meta:\n           indexes = ((('from_user', 'to_user'), True),)\n\nTo find everyone that ``huey`` follows:\n\n.. code-block:: python\n\n   huey = User.get(User.username == 'huey')\n\n   following = (User\n                .select()\n                .join(Relationship, on=Relationship.to_user)\n                .where(Relationship.from_user == huey))\n\nTo find everyone who follows ``huey``:\n\n.. code-block:: python\n\n   followers = (User\n                .select()\n                .join(Relationship, on=Relationship.from_user)\n                .where(Relationship.to_user == huey))\n\nPassing the field instance to ``on=`` tells Peewee which foreign key column to\nuse for the join.\n\nJoining without a foreign key\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nA join can be performed on any two tables, even when no :class:`ForeignKeyField`\nexists between them, by supplying an explicit join predicate as an expression:\n\n.. code-block:: python\n\n   query = (User\n            .select(User, ActivityLog)\n            .join(ActivityLog,\n                  on=(User.id == ActivityLog.object_id),\n                  attr='log')\n            .where(\n                (ActivityLog.activity_type == 'login') &\n                (User.username == 'huey')))\n\n   for user in query:\n       print(user.username, '->', user.log.description)\n\nSelf-joins\n^^^^^^^^^^\n\nA self-join queries a model against an alias of itself. Use :meth:`Model.alias`\nto create the alias:\n\n.. code-block:: python\n\n   # Find all categories and their immediate parent name.\n   class Category(BaseModel):\n       name = TextField()\n       parent = ForeignKeyField('self', null=True, backref='children')\n\n   Parent = Category.alias()\n   query = (Category\n            .select(Category.name, Parent.name)\n            .join(Parent, JOIN.LEFT_OUTER, on=(Category.parent == Parent.id))\n            .order_by(Category.name))\n\n   for row in query:\n       print(row.name, 'parent:', row.parent.name if row.parent else 'None')\n\n.. seealso::\n   Recursive queries over self-referential structures are covered in\n   :ref:`cte` using recursive CTEs.\n\n.. _manytomany:\n\nMany-to-Many Relationships\n--------------------------\n\nA many-to-many relationship - where one row in table A can relate to many rows\nin table B *and vice versa* - requires an intermediate *through table* that\nholds pairs of foreign keys.\n\nManual through table (recommended)\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe explicit approach gives full control over the through table and its\nqueries:\n\n.. code-block:: python\n\n   class Student(BaseModel):\n       name = TextField()\n\n   class Course(BaseModel):\n       title = TextField()\n\n   class Enrollment(BaseModel):\n       \"\"\"Through table linking students to courses.\"\"\"\n       student = ForeignKeyField(Student, backref='enrollments')\n       course = ForeignKeyField(Course, backref='enrollments')\n       enrolled_on = DateField(default=datetime.date.today)\n\n       class Meta:\n           indexes = (\n               (('student', 'course'), True),\n           )\n\nTo query all courses a given student is enrolled in:\n\n.. code-block:: python\n\n   huey = Student.get(Student.name == 'Huey')\n\n   courses = (Course\n              .select()\n              .join(Enrollment)\n              .where(Enrollment.student == huey)\n              .order_by(Course.title))\n\n   for course in courses:\n       print(course.title)\n\nTo query all students in a given course, along with when they enrolled:\n\n.. code-block:: python\n\n   cs101 = Course.get(Course.title == 'CS 101')\n\n   query = (Student\n            .select(Student, Enrollment.enrolled_on)\n            .join(Enrollment)\n            .where(Enrollment.course == cs101)\n            .order_by(Student.name))\n\n   for student in query:\n       print(student.name, student.enrollment.enrolled_on)\n\n   # To attach enrollment date to the Student for simplicity:\n   for student in query.objects():\n       print(student.name, student.enrolled_on)\n\nSince all data is available via the through table model, this approach is most\nflexible and handles any querying requirement without special casing.\n\nManyToManyField\n^^^^^^^^^^^^^^^\n\n:class:`ManyToManyField` provides a shortcut API that manages the through\ntable automatically. It is suitable for simple cases where the through table\nrequires no extra columns and complex querying is not needed.\n\n.. code-block:: python\n\n   class Student(BaseModel):\n       name = TextField()\n\n   class Course(BaseModel):\n       title = TextField()\n       students = ManyToManyField(Student, backref='courses')\n\n   # Retrieve the auto-generated through model if direct access is needed.\n   Enrollment = Course.students.get_through_model()\n\n   db.create_tables([Student, Course, Enrollment])\n\n   huey = Student.create(name='Huey')\n   cs101 = Course.create(title='CS 101')\n\n   # Adding and removing relationships:\n   huey.courses.add(cs101)\n   huey.courses.add(Course.select().where(Course.title.contains('Math')))\n\n   cs101.students.remove(huey)\n   cs101.students.clear()   # Removes all students from this course.\n\n   # Querying through the field:\n   for course in huey.courses.order_by(Course.title):\n       print(course.title)\n\n.. warning::\n   :class:`ManyToManyField` does not work correctly with model inheritance.\n   The through table contains foreign keys back to the original models, and\n   those pointers are not automatically updated for subclasses. For any model\n   that will be subclassed, use an explicit through table instead.\n\n.. seealso::\n   :meth:`ManyToManyField.add`, :meth:`ManyToManyField.remove`,\n   :meth:`ManyToManyField.clear`, :meth:`ManyToManyField.get_through_model`.\n\n\n.. _prefetch:\n\nAvoiding N+1 with Prefetch\n--------------------------\n\nJoins solve the N+1 problem when traversing from the *many* side toward the\n*one* side - for example, fetching tweets with their authors. Each tweet has\nexactly one author, so a join produces exactly one result row per tweet.\n\nThe situation is different when traversing from the *one* side toward the\n*many* side - for example, fetching users *with all their tweets*. A join in\nthis direction produces one result row *per tweet*, which means users with\nmultiple tweets appear multiple times in the result set. Deduplicating those\nrows in application code is awkward and error-prone.\n\n:func:`prefetch` solves this by issuing one query per table, then stitching\nthe results together in Python. Instead of *O(n)* queries for *n* rows, we will\ndo *O(k)* queries for *k* tables:\n\n.. code-block:: python\n\n   # Two queries total, regardless of how many users or tweets there are:\n   # SELECT * FROM user\n   # SELECT * FROM tweet WHERE user_id IN (...)\n   users = User.select().prefetch(Tweet)\n\n   # Equivalent to above.\n   users = prefetch(User.select(), Tweet.select())\n\n   for user in users:\n       print(user.username)\n       for tweet in user.tweets:  # No additional query, user.tweets is a list.\n           print(f'  {tweet.content}')\n\nThe models passed to :func:`prefetch` must be linked by foreign keys.\nPeewee infers the relationships and assigns the prefetched rows to the\nappropriate back-reference attribute on each instance.\n\nPrefetch can span more than two tables. To fetch users, their tweets, and the\nfavorites on each tweet in three queries:\n\n.. code-block:: python\n\n   users = prefetch(User.select(), Tweet.select(), Favorite.select())\n\n   for user in users:\n       for tweet in user.tweets:\n           print(f'{user.username}: {tweet.content} '\n                 f'({len(tweet.favorites)} favorites)')\n\nFiltering prefetched rows\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nBoth the outer query and the prefetch subqueries can carry ``WHERE`` clauses\nand other modifiers independently:\n\n.. code-block:: python\n\n   one_week_ago = datetime.date.today() - datetime.timedelta(days=7)\n\n   users = prefetch(\n       User.select().order_by(User.username),\n       Tweet.select().where(Tweet.timestamp >= one_week_ago),\n   )\n\nThe filter on ``Tweet`` applies only to the prefetched tweets; it does not\naffect which users are returned.\n\nChoosing between joins and prefetch\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nUse a **join** when:\n\n* Traversing from the many side to the one side (tweet -> author).\n* Filtering on columns in the related table (tweets by users whose username\n  starts with \"h\").\n* Only a subset of related fields is needed.\n\nUse **prefetch** when:\n\n* Traversing from the one side to the many side (user -> all their tweets).\n* The full set of related rows is needed for each parent row.\n* Nesting more than one level of related data (users -> tweets -> favorites).\n\n.. note::\n   ``LIMIT`` on the outer query of a :func:`prefetch` call works as\n   expected. Limiting the *inner* queries (the prefetched tables) is not\n   directly supported and requires a manual approach - see\n   :ref:`top-n-per-group` in the recipes document for techniques.\n\n.. seealso::\n   :func:`prefetch` API reference.\n"
  },
  {
    "path": "docs/peewee/schema.rst",
    "content": ".. _schema:\n\nSchema Management\n=================\n\nThis document covers creating and dropping tables, managing indexes and\nconstraints after the fact, and evolving a schema over time.\n\nCreating Tables\n---------------\n\nCreate tables for a list of models with :meth:`Database.create_tables`:\n\n.. code-block:: python\n\n   db.create_tables([User, Tweet, Favorite])\n\nBy default Peewee uses ``CREATE TABLE IF NOT EXISTS``, making it safe to call\non application startup. To disable this, pass ``safe=False``.\n\n.. code-block:: python\n\n   db.create_tables([User, Tweet, Favorite], safe=False)\n\nTo create a single table:\n\n.. code-block:: python\n\n   Tweet.create_table()\n\n:meth:`Database.create_tables` respects foreign key dependencies: if ``Tweet``\nreferences ``User``, ``User``'s table is created first regardless of the order\nin which they appear in the list.\n\nIndexes declared in ``Meta.indexes`` and via :meth:`Model.add_index` are\ncreated along with the table.\n\n.. note::\n   A common pattern in web applications is to call ``db.create_tables(MODELS, safe=True)``\n   once at startup. This ensures all tables exist without failing on an already-\n   initialized database. It does **not** apply schema changes - for that, see\n   :ref:`migrations`.\n\nDropping Tables\n---------------\n\n.. code-block:: python\n\n   db.drop_tables([User, Tweet, Favorite])\n\nBy default Peewee uses ``DROP TABLE IF EXISTS``, making it safe to call\nmultiple times. To disable this, pass ``safe=False``.\n\n.. code-block:: python\n\n   db.drop_tables([User, Tweet, Favorite], safe=False)\n\nPass ``cascade=True`` (Postgresql and MySQL) to let the database handle\ndependency ordering:\n\n.. code-block:: python\n\n   db.drop_tables([User, Tweet, Favorite], cascade=True)\n\nTo drop a single table:\n\n.. code-block:: python\n\n   User.drop_table()\n\nSchemaManager\n-------------\n\n:class:`SchemaManager` provides finer-grained control over DDL operations.\nEach model exposes an instance at ``Model._schema``.\n\nCreating and dropping indexes independently:\n\n.. code-block:: python\n\n   # Create just the indexes for a model (table already exists).\n   User._schema.create_indexes()\n\n   # Drop a specific index.\n   User._schema.drop_index(User.username)\n\nAdding a foreign key constraint after table creation (useful when circular\nforeign keys are involved - see :ref:`circular-fks` in the models document):\n\n.. code-block:: python\n\n   # The table exists but the constraint was deferred.\n   User._schema.create_foreign_key(User.favorite_tweet)\n\n.. note::\n   SQLite does not support adding foreign key constraints to existing tables.\n   On SQLite, ``create_foreign_key`` will result in an\n   :class:`OperationalError`.\n\nTruncating a table:\n\n.. code-block:: python\n\n   User._schema.truncate_table()       # No cascade.\n   User._schema.truncate_table(cascade=True)   # Postgresql only.\n\n.. seealso::\n   :class:`SchemaManager` API reference.\n\n.. _migrations:\n\nSchema Migrations\n-----------------\n\nPeewee does not include a built-in migration system. For schema changes in an\nexisting deployment - adding columns, dropping columns, renaming tables,\nmodifying indexes - use one of the following approaches.\n\nPlayhouse migrate module\n^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe :ref:`migrate <migrate>` module in playhouse provides a set of helper\nfunctions for common schema changes, applied through a :class:`SchemaMigrator`:\n\n.. code-block:: python\n\n   from playhouse.migrate import *\n\n   db = SqliteDatabase(...)\n\n   migrator = SchemaMigrator.from_database(db)\n\n   first_name = TextField(default='')\n   last_name  = TextField(default='')\n\n   with db.atomic():\n       migrate(\n           migrator.add_column('person', 'first_name', first_name),\n           migrator.add_column('person', 'last_name',  last_name),\n           migrator.drop_column('person', 'name'),\n       )\n\nSupported operations:\n\n- Add, rename, or drop columns.\n- Make columns nullable or not nullable.\n- Change a column's type.\n- Rename a table.\n- Add or drop indexes and constraints.\n- Add or drop column default values.\n\n.. seealso::\n   :ref:`migrate` for in-depth examples and API reference.\n\nRaw SQL migrations\n^^^^^^^^^^^^^^^^^^^\n\nFor changes the migrate module does not cover, execute ALTER TABLE statements\ndirectly:\n\n.. code-block:: python\n\n   with db.atomic():\n       db.execute_sql('ALTER TABLE tweet ADD COLUMN view_count INTEGER DEFAULT 0')\n\nSQLite limitations\n^^^^^^^^^^^^^^^^^^\n\nSQLite has limited ALTER TABLE support. It supports ``ADD COLUMN`` and\n``RENAME TABLE`` but not ``DROP COLUMN``, ``RENAME COLUMN``, or constraint\nchanges in older versions (SQLite 3.35.0+ adds ``DROP COLUMN``).\n\nFor more complex SQLite schema changes, the standard workaround is to:\n\n1. Create a new table with the desired schema.\n2. Copy data with ``INSERT INTO new_table SELECT ... FROM old_table``.\n3. Drop the old table.\n4. Rename the new table.\n\nThe playhouse :ref:`migrate <migrate>` module transparently handles the above\nworkaround for older SQLite versions.\n\nIntrospecting an Existing Schema\n----------------------------------\n\n:meth:`Database.get_tables` returns the names of all tables in the database:\n\n.. code-block:: python\n\n   db.get_tables()\n   # ['user', 'tweet', 'favorite']\n\n:meth:`Database.get_columns` returns column metadata for a table as a list of\n:class:`ColumnMetadata` instances:\n\n.. code-block:: python\n\n   for col in db.get_columns('tweet'):\n       print(col.name, col.data_type, col.null)\n\n:meth:`Database.get_indexes` returns index metadata as a list of\n:class:`IndexMetadata` instances:\n\n.. code-block:: python\n\n   for idx in db.get_indexes('user'):\n       print(idx.name, idx.columns, idx.unique)\n\n:meth:`Database.get_foreign_keys` returns foreign key metadata as a list of\n:class:`ForeignKeyMetadata` instances:\n\n.. code-block:: python\n\n   for fk in db.get_foreign_keys('tweet'):\n       print(fk.column, '->', fk.dest_table, fk.dest_column)\n\n:meth:`Database.get_views` returns a list of views in the database as a list of\n:class:`ViewMetadata` instances:\n\n.. code-block:: python\n\n   for view in db.get_views():\n       print(view.name, view.sql)\n\n\nGenerating models from an existing database\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe :ref:`pwiz` command-line tool introspects an existing database and emits\nPython model definitions:\n\n.. code-block:: shell\n\n   python -m pwiz -e postgresql my_database > models.py\n   python -m pwiz -e sqlite my_app.db > models.py\n\nThe generated models can be used directly or as a starting point for further\ncustomization.\n"
  },
  {
    "path": "docs/peewee/sqlite.rst",
    "content": ".. _sqlite:\n\nSQLite\n======\n\nThe core :class:`SqliteDatabase` handles pragmas, user-defined functions,\nWAL mode, full-text search and JSON. Because the full-text search and JSON\nfields are specific to SQLite, these features are provided by ``playhouse.sqlite_ext``.\n\n.. contents:: On this page\n   :local:\n   :depth: 1\n\nImplementations\n---------------\n\n:class:`SqliteDatabase`\n   Core SQLite implementation. Provides:\n\n   * Pragma support (including WAL-mode)\n   * User-defined functions\n   * ATTACH / DETACH databases\n   * Full-text search\n   * JSON\n\n   Full-text search and JSON implementations available in\n   ``playhouse.sqlite_ext``.\n\n:class:`~playhouse.cysqlite_ext.CySqliteDatabase` (``playhouse.cysqlite_ext``)\n   Extends :class:`SqliteDatabase`, uses `cysqlite <https://cysqlite.readthedocs.io/en/latest/>`__ driver.\n\n   * All above functionality\n   * Table-value functions\n   * Commit / Rollback / Update / Progress / Trace hooks\n   * BLOB I/O\n   * Online backups\n   * Can be built `with encryption <https://cysqlite.readthedocs.io/en/latest/installation.html#sqlcipher>`__.\n\n:class:`~playhouse.apsw_ext.APSWDatabase` (``playhouse.apsw_ext``)\n   Extends :class:`SqliteDatabase`, uses `apsw <https://github.com/rogerbinns/apsw/>`__ driver.\n\n   APSW is a thin C-level driver that exposes the full range of SQLite\n   functionality.\n\n:class:`~playhouse.sqlcipher_ext.SqlCipherDatabase` (``playhouse.sqlcipher_ext``)\n   Extends :class:`SqliteDatabase`, uses `sqlcipher3 <https://github.com/coleifer/sqlcipher3>`__ driver.\n\n   SQLCipher provides transparent full-database encryption using 256-bit AES,\n   ensuring data on-disk is secure.\n\n:class:`~playhouse.sqliteq.SqliteQueueDatabase` (``playhouse.sqliteq``)\n   Extends :class:`SqliteDatabase`.\n\n   Provides a SQLite database implementation with a long-lived background\n   writer thread. All write operations are managed by a single write\n   connection, preventing timeouts and database locking issues. This\n   implementation is useful when using Sqlite in multi-threaded environments\n   with frequent writes.\n\n.. _sqlite-pragma:\n\nPRAGMA statements\n-----------------\n\nSQLite allows run-time configuration through ``PRAGMA`` statements (`SQLite documentation <https://www.sqlite.org/pragma.html>`_).\nThese statements are typically run when a new database connection is created.\n\nTo specify default ``PRAGMA`` statements for connections:\n\n.. code-block:: python\n\n   db = SqliteDatabase('my_app.db', pragmas={\n       'journal_mode': 'wal',\n       'cache_size': 10000,  # 10000 pages, or ~40MB\n       'foreign_keys': 1,  # Enforce foreign-key constraints\n   })\n\nPRAGMAs may also be configured dynamically using either the :meth:`~SqliteDatabase.pragma`\nmethod or the special properties exposed on the :class:`SqliteDatabase` object:\n\n.. code-block:: python\n\n   # Set cache size to 64MB for *current connection*.\n   db.pragma('cache_size', -64000)\n\n   # Same as above.\n   db.cache_size = -64000\n\n   # Read the value of several pragmas:\n   print('cache_size:', db.cache_size)\n   print('foreign_keys:', db.foreign_keys)\n   print('journal_mode:', db.journal_mode)\n   print('page_size:', db.page_size)\n\n   # Set foreign_keys pragma on current connection *AND* on all\n   # connections opened subsequently.\n   db.pragma('foreign_keys', 1, permanent=True)\n\n.. attention::\n   Pragmas set using the :meth:`~SqliteDatabase.pragma` method are not\n   re-applied when a new connection opens. To configure a pragma to be\n   run whenever a new connection is opened, specify ``permanent=True``.\n\n   .. code-block:: python\n\n      db.pragma('foreign_keys', 1, permanent=True)\n\n.. seealso::\n   SQLite PRAGMA documentation: https://sqlite.org/pragma.html\n\n.. _sqlite-user-functions:\n\nUser-Defined Functions\n----------------------\n\nSQLite can be extended with user-defined Python code. The\n:class:`SqliteDatabase` class supports a variety of user-defined extensions:\n\nFunctions\n   User-defined functions accept any number of parameters and return a single\n   value.\n\n   * :meth:`SqliteDatabase.register_function`\n   * :meth:`SqliteDatabase.func` - decorator.\n\nAggregates\n   Aggregate values across multiple rows and return a single value.\n\n   * :meth:`SqliteDatabase.register_aggregate`\n   * :meth:`SqliteDatabase.aggregate` - decorator.\n\nWindow Functions\n   Aggregates which support operating on sliding windows of data.\n\n   * :meth:`SqliteDatabase.register_window_function`\n   * :meth:`SqliteDatabase.window_function` - decorator.\n\nCollations\n   Control how values are ordered and sorted.\n\n   * :meth:`SqliteDatabase.register_collation`\n   * :meth:`SqliteDatabase.collation` - decorator.\n\nTable Functions\n   User-defined tables (requres ``cysqlite``).\n\n   * :meth:`.CySqliteDatabase.register_table_function`\n   * :meth:`.CySqliteDatabase.table_function` - decorator.\n\nShared Libraries\n   Load an extension from a shared library.\n\n   * :meth:`SqliteDatabase.load_extension`\n   * :meth:`SqliteDatabase.unload_extension`\n\nFunction example\n^^^^^^^^^^^^^^^^\n\n.. code-block:: python\n\n   db = SqliteDatabase('analytics.db')\n\n   from urllib.parse import urlparse\n\n   @db.func('hostname')\n   def hostname(url):\n       if url is not None:\n           return urlparse(url).netloc\n\n   # Call this function in our code:\n   # The following finds the most common hostnames of referrers by count:\n   query = (PageView\n            .select(fn.hostname(PageView.referrer), fn.COUNT(PageView.id))\n            .group_by(fn.hostname(PageView.referrer))\n            .order_by(fn.COUNT(PageView.id).desc()))\n\nAggregate example\n^^^^^^^^^^^^^^^^^\n\nUser-defined aggregates must define two methods:\n\n* ``step(*values)`` - called once for each row being aggregated.\n* ``finalize()`` - called only once to produce final aggregate value.\n\n.. code-block:: python\n\n   from hashlib import md5\n\n   @db.aggregate('md5')\n   class MD5Checksum(object):\n       def __init__(self):\n           self.checksum = md5()\n\n       def step(self, value):\n           self.checksum.update(value.encode('utf-8'))\n\n       def finalize(self):\n           return self.checksum.hexdigest()\n\n   # Usage:\n   # The following computes an aggregate MD5 checksum for files broken\n   # up into chunks and stored in the database.\n   query = (FileChunk\n            .select(FileChunk.filename, fn.MD5(FileChunk.data))\n            .group_by(FileChunk.filename)\n            .order_by(FileChunk.filename, FileChunk.sequence))\n\nWindow function example\n^^^^^^^^^^^^^^^^^^^^^^^\n\nUser-defined window functions are simply aggregates with two additional\nmethods:\n\n* ``step(*values)`` - called for each row being aggregated.\n* ``inverse(*values)`` - \"invert\" the effect of a call to ``step(*values)``.\n* ``value()`` - return the current value of the aggregate.\n* ``finalize()`` - return final aggregate value.\n\n.. code-block:: python\n\n   # Window functions are normal aggregates with two additional methods:\n   # inverse(value) - Perform the inverse of step(value).\n   # value() - Report value at current step.\n   @db.aggregate('mysum')\n   class MySum(object):\n       def __init__(self):\n           self._value = 0\n       def step(self, value):\n           self._value += (value or 0)\n       def inverse(self, value):\n           self._value -= (value or 0)  # Do opposite of \"step()\".\n       def value(self):\n           return self._value\n       def finalize(self):\n           return self._value\n\n   # e.g., aggregate sum of employee salaries over their department.\n   query = (Employee\n            .select(\n                Employee.department,\n                Employee.salary,\n                fn.mysum(Employee.salary).over(\n                    partition_by=[Employee.department]))\n            .order_by(Employee.id))\n\nCollation example\n^^^^^^^^^^^^^^^^^\n\nCollations accept two values and provide a value indicating how they should be\nordered (e.g. ``cmp(lhs, rhs)``).\n\n.. code-block:: python\n\n   @db.collation('ireverse')\n   def collate_reverse(s1, s2):\n       # Case-insensitive reverse.\n       s1, s2 = s1.lower(), s2.lower()\n       return (s1 < s2) - (s1 > s2)  # Equivalent to -cmp(s1, s2)\n\n   # To use this collation to sort books in reverse order...\n   Book.select().order_by(collate_reverse.collation(Book.title))\n\n   # Or...\n   Book.select().order_by(Book.title.asc(collation='reverse'))\n\nTable function example\n^^^^^^^^^^^^^^^^^^^^^^\n\nExample user-defined table-value function (see `cysqlite TableFunction docs <https://cysqlite.readthedocs.io/en/latest/api.html#tablefunction>`_\nfor full details on ``TableFunction``).\n\n.. code-block:: python\n\n   from cysqlite import TableFunction\n   from playhouse.cysqlite_ext import CySqliteDatabase\n\n   db = CySqliteDatabase('my_app.db')\n\n   @db.table_function('series')\n   class Series(TableFunction):\n       columns = ['value']\n       params = ['start', 'stop', 'step']\n\n       def initialize(self, start=0, stop=None, step=1):\n           \"\"\"\n           Table-functions declare an initialize() method, which is\n           called with whatever arguments the user has called the\n           function with.\n           \"\"\"\n           self.start = self.current = start\n           self.stop = stop or float('Inf')\n           self.step = step\n\n       def iterate(self, idx):\n           \"\"\"\n           Iterate is called repeatedly by the SQLite database engine\n           until the required number of rows has been read **or** the\n           function raises a `StopIteration` signalling no more rows\n           are available.\n           \"\"\"\n           if self.current > self.stop:\n               raise StopIteration\n\n           ret, self.current = self.current, self.current + self.step\n           return (ret,)\n\n   # Usage:\n   cursor = db.execute_sql('SELECT * FROM series(?, ?, ?)', (0, 5, 2))\n   for value, in cursor:\n       print(value)\n\n   # Prints:\n   # 0\n   # 2\n   # 4\n\nShared Libraries\n^^^^^^^^^^^^^^^^\n\nExample:\n\n   .. code-block:: python\n\n      # Load `closure.so` shared library in the current directory.\n      db = SqliteDatabase('my_app.db')\n      db.load_extension('closure')\n\nTo support shared libraries, your SQLite3 will need to have been compiled with\nsupport for run-time loadable extensions.\n\n.. _sqlite-locking-mode:\n\nLocking Mode for Transactions\n-----------------------------\n\nSQLite transactions can be opened in three different modes:\n\n* *Deferred* (**default**) - only acquires lock when a read or write is\n  performed. The first read creates a `shared lock <https://sqlite.org/lockingv3.html#locking>`_\n  and the first write creates a `reserved lock <https://sqlite.org/lockingv3.html#locking>`_.\n  Because the acquisition of the lock is deferred until actually needed, it is\n  possible that another thread or process could create a separate transaction\n  and write to the database.\n* *Immediate* - a `reserved lock <https://sqlite.org/lockingv3.html#locking>`_\n  is acquired immediately. In this mode, no other connection may write to the\n  database or open an *immediate* or *exclusive* transaction. Other processes\n  can continue to read from the database, however.\n* *Exclusive* - opens an `exclusive lock <https://sqlite.org/lockingv3.html#locking>`_\n  which prevents all (except for read uncommitted) connections from accessing\n  the database until the transaction is complete.\n\nExample specifying the locking mode:\n\n.. code-block:: python\n\n    db = SqliteDatabase('app.db')\n\n    with db.atomic('EXCLUSIVE'):\n        read()\n        write()\n\n\n    @db.atomic('IMMEDIATE')\n    def some_other_function():\n        # This function is wrapped in an \"IMMEDIATE\" transaction.\n        do_something_else()\n\nFor more information, see the SQLite `locking documentation <https://sqlite.org/lockingv3.html#locking>`_.\nTo learn more about transactions in Peewee, see the :ref:`transactions`\ndocumentation.\n\n.. danger::\n   Do not alter the ``isolation_level`` property of the ``sqlite3.Connection``\n   object. Peewee requires the ``sqlite3`` driver be in autocommit-mode, which\n   is handled automatically by :class:`SqliteDatabase`.\n\n\n.. _cysqlite-ext:\n\nCySqlite\n--------\n\n.. module:: playhouse.cysqlite_ext\n\n:class:`CySqliteDatabase` uses the `cysqlite <https://cysqlite.readthedocs.io>`_\ndriver, a high-performance alternative to the standard library ``sqlite3``\nmodule. ``cysqlite`` provides additional features and hooks not available with\nin the standard library ``sqlite3`` driver.\n\nInstallation:\n\n.. code-block:: shell\n\n   pip install cysqlite\n\nUsage:\n\n.. code-block:: python\n\n   from playhouse.cysqlite_ext import CySqliteDatabase\n\n   db = CySqliteDatabase('my_app.db', pragmas={\n       'cache_size': -64000,\n       'journal_mode': 'wal',\n       'foreign_keys': 1,\n   })\n\n.. class:: CySqliteDatabase(database, **kwargs)\n\n   :param list pragmas: A list of 2-tuples containing pragma key and value to\n       set every time a connection is opened.\n   :param timeout: Set the busy-timeout on the SQLite driver (in seconds).\n   :param bool rank_functions: Make search result ranking functions available.\n       Recommended only when using FTS4.\n   :param bool regexp_function: Make the REGEXP function available.\n\n   .. seealso::\n      CySqliteDatabase extends :class:`SqliteDatabase` and inherits all\n      methods for declaring user-defined functions, aggregates, window\n      functions, collations, pragmas, etc.\n\n   Example:\n\n   .. code-block:: python\n\n       db = CySqliteDatabase('app.db', pragmas={'journal_mode': 'wal'})\n\n   .. method:: table_function(name)\n\n      Class-decorator for registering a ``cysqlite.TableFunction``. Table\n      functions are user-defined functions that, rather than returning a\n      single, scalar value, can return any number of rows of tabular data.\n\n      See `cysqlite docs <https://cysqlite.readthedocs.io/en/latest/api.html#tablefunction>`__ for details on\n      ``TableFunction`` API.\n\n      .. code-block:: python\n\n         from cysqlite import TableFunction\n\n         @db.table_function('series')\n         class Series(TableFunction):\n             columns = ['value']\n             params = ['start', 'stop', 'step']\n\n             def initialize(self, start=0, stop=None, step=1):\n                 \"\"\"\n                 Table-functions declare an initialize() method, which is\n                 called with whatever arguments the user has called the\n                 function with.\n                 \"\"\"\n                 self.start = self.current = start\n                 self.stop = stop or float('Inf')\n                 self.step = step\n\n             def iterate(self, idx):\n                 \"\"\"\n                 Iterate is called repeatedly by the SQLite database engine\n                 until the required number of rows has been read **or** the\n                 function raises a `StopIteration` signalling no more rows\n                 are available.\n                 \"\"\"\n                 if self.current > self.stop:\n                     raise StopIteration\n\n                 ret, self.current = self.current, self.current + self.step\n                 return (ret,)\n\n         cursor = db.execute_sql('SELECT * FROM series(?, ?, ?)', (0, 5, 2))\n         for (value,) in cursor:\n             print(value)\n\n         # Prints:\n         # 0\n         # 2\n         # 4\n\n   .. method:: register_table_function(klass, name)\n\n      :param TableFunction klass: class implementing TableFunction API.\n      :param str name: name for user-defined table function.\n\n      Register a ``cysqlite.TableFunction`` class with the connection. Table\n      functions are user-defined functions that, rather than returning a\n      single, scalar value, can return any number of rows of tabular data.\n\n      .. seealso::\n         * :meth:`CySqliteDatabase.table_function` for example implementation.\n         * `cysqlite docs <https://cysqlite.readthedocs.io/en/latest/api.html#tablefunction>`__\n           for details on ``TableFunction`` API.\n\n   .. method:: unregister_table_function(name)\n\n      :param name: Name of the user-defined table function.\n      :returns: True or False, depending on whether the function was removed.\n\n      Unregister the user-defined table function.\n\n   .. method:: on_commit(fn)\n\n      :param fn: callable or ``None`` to clear the current hook.\n\n      Register a callback to be executed whenever a transaction is committed\n      on the current connection. The callback accepts no parameters and the\n      return value is ignored.\n\n      However, if the callback raises a :class:`ValueError`, the\n      transaction will be aborted and rolled-back.\n\n      Example:\n\n      .. code-block:: python\n\n         db = CySqliteDatabase(':memory:')\n\n         @db.on_commit\n         def on_commit():\n             logger.info('COMMITing changes')\n\n   .. method:: on_rollback(fn)\n\n      :param fn: callable or ``None`` to clear the current hook.\n\n      Register a callback to be executed whenever a transaction is rolled\n      back on the current connection. The callback accepts no parameters and\n      the return value is ignored.\n\n      Example:\n\n      .. code-block:: python\n\n         @db.on_rollback\n         def on_rollback():\n             logger.info('Rolling back changes')\n\n   .. method:: on_update(fn)\n\n      :param fn: callable or ``None`` to clear the current hook.\n\n      Register a callback to be executed whenever the database is written to\n      (via an *UPDATE*, *INSERT* or *DELETE* query). The callback should\n      accept the following parameters:\n\n      * ``query`` - the type of query, either *INSERT*, *UPDATE* or *DELETE*.\n      * database name - the default database is named *main*.\n      * table name - name of table being modified.\n      * rowid - the rowid of the row being modified.\n\n      The callback's return value is ignored.\n\n      Example:\n\n      .. code-block:: python\n\n         db = CySqliteDatabase(':memory:')\n\n         @db.on_update\n         def on_update(query_type, db, table, rowid):\n             # e.g. INSERT row 3 into table users.\n             logger.info('%s row %s into table %s', query_type, rowid, table)\n\n   .. method:: authorizer(fn)\n\n      :param fn: callable or ``None`` to clear the current authorizer.\n\n      Register an authorizer callback. Authorizer callbacks must accept 5\n      parameters, which vary depending on the operation being checked.\n\n      * op: operation code, e.g. ``cysqlite.SQLITE_INSERT``.\n      * p1: operation-specific value, e.g. table name for ``SQLITE_INSERT``.\n      * p2: operation-specific value.\n      * p3: database name, e.g. ``\"main\"``.\n      * p4: inner-most trigger or view responsible for the access attempt if\n        applicable, else ``None``.\n\n      See `sqlite authorizer documentation <https://www.sqlite.org/c3ref/c_alter_table.html>`_\n      for description of authorizer codes and values for parameters p1 and p2.\n\n      The authorizer callback must return one of:\n\n      * ``cysqlite.SQLITE_OK``: allow operation.\n      * ``cysqlite.SQLITE_IGNORE``: allow statement compilation but prevent\n        the operation from occuring.\n      * ``cysqlite.SQLITE_DENY``: prevent statement compilation.\n\n      More details can be found in the `cysqlite docs <https://cysqlite.readthedocs.io/en/latest/api.html#Connection.authorizer>`__.\n\n   .. method:: trace(fn, mask=2, expand_sql=True):\n\n      :param fn: callable or ``None`` to clear the current trace hook.\n      :param int mask: mask of what types of events to trace. Default value\n          corresponds to ``SQLITE_TRACE_PROFILE``.\n      :param bool expand_sql: Pass callback the ``sqlite3_expanded_sql()``\n          from ``sqlite3_stmt`` (expands bound parameters)\n\n      Register a trace hook (``sqlite3_trace_v2``). Trace callback must\n      accept 4 parameters, which vary depending on the operation being\n      traced.\n\n      * event: type of event, e.g. ``SQLITE_TRACE_PROFILE``.\n      * sid: memory address of statement (only ``SQLITE_TRACE_CLOSE``), else -1.\n      * sql: SQL string. If ``expand_sql`` then bound parameters will be\n        expanded (for ``SQLITE_TRACE_CLOSE``, ``sql=None``).\n      * ns: estimated number of nanoseconds the statement took to run (only\n        ``SQLITE_TRACE_PROFILE``), else -1.\n\n      Any return value from callback is ignored.\n\n      More details can be found in the `cysqlite docs <https://cysqlite.readthedocs.io/en/latest/api.html#Connection.trace>`__.\n\n   .. method:: slow_query_log(threshold_ms=50, logger=None, level=logging.WARNING, expand_sql=True)\n\n      :param threshold_ms: estimated millisecond threshold to log slow queries.\n      :param logger: logging namespace, defaults to ``'peewee.cysqlite_ext'``.\n      :param int level: level for slow query log.\n      :param bool expand_sql: expand bound parameters in SQL query.\n\n      Register a ``sqlite3_trace_v2`` callback that will log slow queries to\n      the given logger. Overrides previously-registered :py:meth:`~CySqliteDatabase.trace`\n      callback. Automatically re-registered when new connection is opened.\n\n   .. method:: progress(fn, n=1)\n\n      :param fn: callable or ``None`` to clear the current progress handler.\n      :param int n: approximate number of VM instructions to execute between\n        calls to the progress handler.\n\n      Register a progress handler (``sqlite3_progress_handler``). Callback\n      takes no arguments and returns 0 to allow progress to continue or any\n      non-zero value to interrupt progress.\n\n      More details can be found in the `cysqlite docs <https://cysqlite.readthedocs.io/en/latest/api.html#Connection.progress>`__.\n\n   .. attribute:: autocommit\n\n      Property which returns a boolean indicating if autocommit is enabled.\n      By default, this value will be ``True`` except when inside a\n      transaction (or :meth:`~Database.atomic` block).\n\n      Example:\n\n      .. code-block:: pycon\n\n         >>> db = CySqliteDatabase(':memory:')\n         >>> db.autocommit\n         True\n         >>> with db.atomic():\n         ...     print(db.autocommit)\n         ...\n         False\n         >>> db.autocommit\n         True\n\n   .. method:: backup(destination, pages=None, name=None, progress=None)\n\n      :param CySqliteDatabase destination: Database object to serve as\n          destination for the backup.\n      :param int pages: Number of pages per iteration. Default value of -1\n          indicates all pages should be backed-up in a single step.\n      :param str name: Name of source database (may differ if you used ATTACH\n          DATABASE to load multiple databases). Defaults to \"main\".\n      :param progress: Progress callback, called with three parameters: the\n          number of pages remaining, the total page count, and whether the\n          backup is complete.\n\n      Example:\n\n      .. code-block:: python\n\n         master = CySqliteDatabase('master.db')\n         replica = CySqliteDatabase('replica.db')\n\n         # Backup the contents of master to replica.\n         master.backup(replica)\n\n   .. method:: backup_to_file(filename, pages, name, progress)\n\n      :param filename: Filename to store the database backup.\n      :param int pages: Number of pages per iteration. Default value of -1\n          indicates all pages should be backed-up in a single step.\n      :param str name: Name of source database (may differ if you used ATTACH\n          DATABASE to load multiple databases). Defaults to \"main\".\n      :param progress: Progress callback, called with three parameters: the\n          number of pages remaining, the total page count, and whether the\n          backup is complete.\n\n      Backup the current database to a file. The backed-up data is not a\n      database dump, but an actual SQLite database file.\n\n      Example:\n\n      .. code-block:: python\n\n         db = CySqliteDatabase('app.db')\n\n         def nightly_backup():\n             filename = 'backup-%s.db' % (datetime.date.today())\n             db.backup_to_file(filename)\n\n   .. method:: blob_open(table, column, rowid, read_only=False)\n\n      :param str table: Name of table containing data.\n      :param str column: Name of column containing data.\n      :param int rowid: ID of row to retrieve.\n      :param bool read_only: Open the blob for reading only.\n      :param str dbname: Database name (e.g. if multiple databases attached).\n      :returns: ``cysqlite.Blob`` instance which provides efficient access to\n          the underlying binary data.\n      :rtype: cysqlite.Blob\n\n      See `cysqlite documentation <https://cysqlite.readthedocs.io/en/latest/api.html#blob>`_ for\n      more details.\n\n      Example:\n\n      .. code-block:: python\n\n          class Image(Model):\n              filename = TextField()\n              data = BlobField()\n\n          buf_size = 1024 * 1024 * 8  # Allocate 8MB for storing file.\n          rowid = Image.insert({\n              Image.filename: 'thefile.jpg',\n              Image.data: fn.zeroblob(buf_size),\n          }).execute()\n\n          # Open the blob, returning a file-like object.\n          blob = db.blob_open('image', 'data', rowid)\n\n          # Write some data to the blob.\n          blob.write(image_data)\n          img_size = blob.tell()\n\n          # Read the data back out of the blob.\n          blob.seek(0)\n          image_data = blob.read(img_size)\n\n\n.. class:: PooledCySqliteDatabase(database, **kwargs)\n\n   Connection-pooling variant of :class:`CySqliteDatabase`.\n\n.. _apsw:\n\nAPSW\n----\n\n.. module:: playhouse.apsw_ext\n\n`APSW <https://rogerbinns.github.io/apsw/>`__ is a thin C wrapper over\nSQLite's C API that exposes nearly every SQLite feature including virtual\ntables, virtual filesystems, and BLOB I/O.\n\nInstallation:\n\n.. code-block:: shell\n\n   pip install apsw\n\nUsage:\n\n.. code-block:: python\n\n   from playhouse.apsw_ext import APSWDatabase\n\n   db = APSWDatabase('my_app.db')\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\nUse the ``Field`` subclasses from ``playhouse.apsw_ext`` rather than those\nfrom ``peewee`` to ensure correct type adaptation. For example, use\n``playhouse.apsw_ext.DateTimeField`` instead of ``peewee.DateTimeField``.\n\n\n.. class:: APSWDatabase(database, **connect_kwargs)\n\n   Subclass of :class:`SqliteDatabase` using the APSW driver.\n\n   :param string database: filename of sqlite database\n   :param connect_kwargs: keyword arguments passed to apsw when opening a connection\n\n   .. method:: register_module(mod_name, mod_inst)\n\n      Register a virtual table module globally. See the `APSW virtual table\n      documentation <https://rogerbinns.github.io/apsw/vtable.html>`_.\n\n      :param string mod_name: name to use for module\n      :param object mod_inst: an object implementing the `Virtual Table <http://rogerbinns.github.io/apsw/vtable.html#vttable-class>`_ interface\n\n   .. method:: unregister_module(mod_name)\n\n      Unregister a previously registered module.\n\n\n.. _sqlcipher:\n\nSQLCipher\n---------\n\n.. module:: playhouse.sqlcipher_ext\n\n`SQLCipher <https://www.zetetic.net/sqlcipher/>`__ is an encrypted wrapper\naround SQLite. Peewee exposes it through :class:`SqlCipherDatabase`, which\nis API-identical to :class:`SqliteDatabase` except for its constructor.\n\nInstallation:\n\n.. code-block:: shell\n\n   pip install sqlcipher3\n\nUsage:\n\n.. code-block:: python\n\n   from playhouse.sqlcipher_ext import SqlCipherDatabase\n\n   db = SqlCipherDatabase(\n       'app.db',\n       passphrase=os.environ['PASSPHRASE'],\n       pragmas={'cache_size': -64000})\n\nExample usage with deferred initialization and passphrase prompt:\n\n.. code-block:: python\n\n   db = SqlCipherDatabase(None)\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\n   class Secret(BaseModel):\n       value = TextField()\n\n   # Prompt the user and initialize the database with their passphrase.\n   while True:\n       db.init('my_app.db', passphrase=input('Passphrase: '))\n       try:\n           db.get_tables()  # Will raise if passphrase is wrong.\n           break\n       except DatabaseError as exc:\n           print('Wrong passphrase.')\n           db.init(None)\n\nPragma configuration (e.g. increasing PBKDF2 iterations):\n\n.. code-block:: python\n\n   db = SqlCipherDatabase('my_app.db',\n                          passphrase='s3cr3t',\n                          pragmas={'kdf_iter': 1_000_000})\n\nSQLCipher can be configured using a number of extension PRAGMAs. The list\nof PRAGMAs and their descriptions can be found in the `SQLCipher documentation <https://www.zetetic.net/sqlcipher/sqlcipher-api/>`__.\n\n.. class:: SqlCipherDatabase(database, passphrase, **kwargs)\n\n   :param str database: Path to the encrypted database file.\n   :param str passphrase: Encryption passphrase (should be 8 character minimum;\n       enforce stronger requirements in your application).\n\n   If the database file does not exist, it is created and encrypted with a\n   key derived from ``passphrase``. If it does exist, ``passphrase`` must\n   match the one used when the file was created.\n\n   If the passphrase is incorrect, an error will be raised when first\n   attempting to access the database (typically ``DatabaseError: file is not a\n   database``).\n\n   .. method:: rekey(passphrase)\n\n      Change the encryption passphrase for the open database.\n\n.. _sqliteq:\n\nSqliteQueueDatabase\n-------------------\n\n.. module:: playhouse.sqliteq\n\n:class:`SqliteQueueDatabase` serializes all write queries through a\nsingle long-lived connection on a dedicated background thread. This allows\nmultiple application threads to write to a SQLite database concurrently\nwithout conflict or timeout errors.\n\n``SqliteQueueDatabase`` can be used as a drop-in replacement for the regular\n:class:`SqliteDatabase` if you want simple **read and write** access to a\nSQLite database from multiple threads, and do not need transactions.\n\n.. code-block:: python\n\n   from playhouse.sqliteq import SqliteQueueDatabase\n\n   db = SqliteQueueDatabase(\n       'my_app.db',\n       use_gevent=False,    # Use stdlib threading (default).\n       autostart=True,      # Start the writer thread immediately.\n       queue_max_size=64,   # Max pending writes before blocking.\n       results_timeout=5.0, # Seconds to wait for a write to complete.\n       pragmas={'journal_mode': 'wal'})\n\nIf you set ``autostart=False``, start the writer thread explicitly:\n\n.. code-block:: python\n\n   db.start()\n\nStop the writer thread on application shutdown (waits for pending writes):\n\n.. code-block:: python\n\n   import atexit\n\n   @atexit.register\n   def _stop():\n       db.stop()\n\nRead queries work as normal. Open and close the connection per-request as you\nwould with any other database. Only writes are funneled through the queue.\n\n**Transactions are not supported.** Because writes from different threads\nare interleaved, there is no way to guarantee that the statements in a\ntransaction from one thread execute atomically without statements from\nanother thread appearing between them. The ``atomic()`` and\n``transaction()`` methods raise a ``ValueError`` if called.\n\nIf you need to temporarily bypass the queue and write directly (for\nexample, during a batch import), use :meth:`~SqliteQueueDatabase.pause`\nand :meth:`~SqliteQueueDatabase.unpause`.\n\n.. class:: SqliteQueueDatabase(database, use_gevent=False, autostart=True, queue_max_size=None, results_timeout=None, **kwargs)\n\n   :param str database: database filename.\n   :param bool use_gevent: use gevent instead of ``threading``.\n   :param bool autostart: automatically start writer background thread.\n   :param int queue_max_size: maximum size of pending writes queue.\n   :param float results_timeout: timeout for waiting for query results from\n       write thread (seconds).\n\n   .. method:: start()\n\n      Start the background writer thread.\n\n   .. method:: stop()\n\n      Signal the writer thread to stop. Blocks until all pending writes\n      are flushed.\n\n   .. method:: is_stopped()\n\n      Return ``True`` if the writer thread is not running.\n\n   .. method:: pause()\n\n      Block until the writer thread finishes its current work, then\n      disconnect it. The calling thread takes over direct database access.\n      Must be followed by a call to :meth:`~SqliteQueueDatabase.unpause`.\n\n   .. method:: unpause()\n\n      Resume the writer thread and reconnect the queue.\n\n\n.. _sqlite-fields:\n\nSQLite-Specific Fields\n----------------------\n\n.. module:: playhouse.sqlite_ext\n\nThese field classes live in ``playhouse.sqlite_ext`` and can be used with:\n\n* :class:`SqliteDatabase`\n* :class:`.CySqliteDatabase`\n* :class:`.APSWDatabase`\n* :class:`.SqlCipherDatabase`\n* :class:`.SqliteQueueDatabase`\n\n.. class:: RowIDField()\n\n   Primary-key field mapped to SQLite's implicit ``rowid`` column.\n\n   For more information, see the SQLite documentation on `rowid tables <https://www.sqlite.org/rowidtable.html>`_.\n\n   .. code-block:: python\n\n      class Note(Model):\n          rowid = RowIDField()  # Implied primary_key=True.\n          content = TextField()\n          timestamp = TimestampField()\n\n   RowIDField can be mapped to a different field name, but it's underlying\n   column name will always be ``rowid``.\n\n   .. code-block:: python\n\n      class Note(Model):\n          id = RowIDField()\n          ...\n\n.. class:: AutoIncrementField()\n\n   Integer primary key that uses SQLite's ``AUTOINCREMENT`` keyword,\n   guaranteeing the primary key is always strictly increasing even after\n   deletions. Has a small performance cost versus the default\n   :class:`PrimaryKeyField` or :class:`RowIDField`.\n\n   See the `SQLite AUTOINCREMENT documentation <https://sqlite.org/autoinc.html>`_ for details.\n\n.. class:: ISODateTimeField()\n\n   Subclass of :class:`DateTimeField` that preserves UTC offset\n   information for timezone-aware datetimes when storing to SQLite's\n   text-based datetime representation.\n\n.. class:: TDecimalField(max_digits=10, decimal_places=5, auto_round=False, rounding=None, *args, **kwargs)\n\n   Subclass of :class:`DecimalField` that stores decimal values in a\n   ``TEXT`` column to avoid any potential loss of precision that may occur when\n   storing in a ``REAL`` (double-precision floating point) column. SQLite does\n   not have a true numeric type, so this field ensures no precision is lost\n   when using Decimals.\n\n.. _sqlite-json:\n\nSQLite JSON\n-----------\n\n:class:`~playhouse.sqlite_ext.JSONField` enables storing and querying JSON data\nin SQLite using the `SQLite json functions <https://sqlite.org/json1.html>`_.\n\n.. class:: JSONField(json_dumps=None, json_loads=None, **kwargs)\n\n   :param json_dumps: Custom JSON serializer. Defaults to ``json.dumps``.\n   :param json_loads: Custom JSON deserializer. Defaults to ``json.loads``.\n\n   Stores and retrieves JSON data transparently and provides efficient\n   implementations for in-place modification and querying. Data is\n   automatically serialized on write, deserialized on read.\n\n   Example model:\n\n   .. code-block:: python\n\n      from peewee import *\n      from playhouse.sqlite_ext import JSONField\n\n      db = SqliteDatabase(':memory:')\n\n      class Config(db.Model):\n          data = JSONField()\n\n      Config.create_table()\n\n      # Create two rows.\n      Config.create(data={'timeout': 30, 'retry': {'max': 5}})\n      Config.create(data={'timeout': 10, 'retry': {'max': 10}})\n\n   To access or modify specific object keys or array indexes in a JSON\n   structure, you can treat the :class:`JSONField` as if it were a\n   dictionary/list:\n\n   .. code-block:: python\n\n      # Select or order by a JSON value:\n      query = (Config\n               .select(Config, Config.data['timeout'].alias('timeout'))\n               .order_by(Config.data['timeout'].desc()))\n\n      # Aggregate on nested value:\n      avg = (Config\n             .select(fn.SUM(Config.data['timeout']) / fn.COUNT(Config.id))\n             .scalar())\n\n      # Filter by nested value:\n      Config.select().where(Config.data['retry']['max'] < 8)\n\n   Data can be atomically updated, written and removed in-place:\n\n   .. code-block:: python\n\n      # In-place update (preserves other keys):\n      (Config\n       .update(data=Config.data.update({'timeout': 60}))\n       .where(Config.data['timeout'] >= 30)\n       .execute())\n\n      # Set a specific path:\n      (Config\n       .update(data=Config.data['timeout'].set(120))\n       .where(Config.data['retry']['max'] == 5)\n       .execute())\n\n      # Update a specific path with an object. Existing field (\"max\") will be\n      # preserved in this example.\n      (Config\n       .update(data=Config.data['retry'].update({'backoff': 1}))\n       .execute())\n\n      # To overwrite a specific path with an object, use set():\n      (Config\n       .update(data=Config.data['retry'].set({'allowed': 10}))\n       .execute())\n\n      # Remove a key atomically:\n      (Config\n       .update(data=Config.data.update({'retry': None}))\n       .where(Config.id == 1)\n       .execute())\n\n      # Another way to remove atomically:\n      (Config\n       .update(data=Config.data['retry'].remove())\n       .where(Config.id == 2)\n       .execute())\n\n   Helpers for other JSON scenarios:\n\n   .. code-block:: python\n\n      # Query JSON types:\n      query = (Config\n               .select(Config.data.json_type(), Config.data['timeout'].json_type())\n               .tuples())\n      # [('object', 'integer'), ('object', 'integer')]\n\n      # Query length of an array:\n      cfg1 = Config.create(data={'statuses': [1, 99, 1, 1]})\n      cfg2 = Config.create(data={'statuses': [1, 1]})\n\n      query = (Config\n               .select(\n                   Config.data['statuses'],\n                   Config.data['statuses'].length())\n               .tuples())\n\n      # [([1, 99, 1, 1], 4), ([1, 1], 2)]\n\n   Let's add a nested value and then see how to iterate through it's contents\n   recursively using the :meth:`~JSONField.tree` method:\n\n   .. code-block:: python\n\n      Config.create(data={'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]})\n\n      tree = Config.data.tree().alias('tree')\n      query = (Config\n               .select(Config.id, tree.c.fullkey, tree.c.value)\n               .from_(Config, tree))\n\n      for row in query.tuples():\n          print(row)\n\n      (1, '$', {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': [1, 2]}),\n      (1, '$.x2', [1, 2]),\n      (1, '$.x2[0]', 1),\n      (1, '$.x2[1]', 2),\n      (1, '$.x1', {'y1': 'z1', 'y2': 'z2'}),\n      (1, '$.x1.y1', 'z1'),\n      (1, '$.x1.y2', 'z2')]\n\n   The :meth:`~JSONField.tree` and :meth:`~JSONField.children` methods\n   are powerful. For more information on how to utilize them, see the\n   `json1 extension documentation <http://sqlite.org/json1.html#jtree>`_.\n\n   .. method:: __getitem__(item)\n\n      :param item: Access a specific key or array index in the JSON data.\n      :return: a special object exposing access to the JSON data.\n      :rtype: JSONPath\n\n      Access a specific key or array index in the JSON data. Returns a\n      :class:`JSONPath` object, which exposes convenient methods for\n      reading or modifying a particular part of a JSON object.\n\n      Example:\n\n      .. code-block:: python\n\n         # If metadata contains {\"tags\": [\"list\", \"of\", \"tags\"]}, we can\n         # extract the first tag in this way:\n         Post.select(Post, Post.metadata['tags'][0].alias('first_tag'))\n\n      For more examples see the :class:`JSONPath` API documentation.\n\n   .. method:: extract(*paths)\n\n      :param paths: One or more JSON paths to extract.\n\n      Extract one or more JSON path values. Returns a list when multiple\n      paths are given.\n\n   .. method:: extract_json(path)\n\n      :param str path: JSON path\n\n      Extract the value at the specified path as a JSON data-type. This\n      corresponds to the ``->`` operator added in Sqlite 3.38.\n\n   .. method:: extract_text(path)\n\n      :param str path: JSON path\n\n      Extract the value at the specified path as a SQL data-type. This\n      corresponds to the ``->>`` operator added in Sqlite 3.38.\n\n   .. method:: set(value, as_json=None)\n\n      :param value: a scalar value, list, or dictionary.\n      :param bool as_json: force the value to be treated as JSON, in which\n          case it will be serialized as JSON in Python beforehand. By\n          default, lists and dictionaries are treated as JSON to be\n          serialized, while strings and integers are passed as-is.\n\n      Set the value stored in a :class:`JSONField`.\n\n      Uses the `json_set() <http://sqlite.org/json1.html#jset>`_ function\n      from the json1 extension.\n\n   .. method:: replace(value, as_json=None)\n\n      :param value: a scalar value, list, or dictionary.\n      :param bool as_json: force the value to be treated as JSON, in which\n          case it will be serialized as JSON in Python beforehand. By\n          default, lists and dictionaries are treated as JSON to be\n          serialized, while strings and integers are passed as-is.\n\n      Replace the existing value stored in a :class:`JSONField`. Will not\n      create if does not exist.\n\n      Uses the `json_replace() <http://sqlite.org/json1.html#jset>`_ function\n      from the json1 extension.\n\n   .. method:: insert(value, as_json=None)\n\n      :param value: a scalar value, list, or dictionary.\n      :param bool as_json: force the value to be treated as JSON, in which\n          case it will be serialized as JSON in Python beforehand. By\n          default, lists and dictionaries are treated as JSON to be\n          serialized, while strings and integers are passed as-is.\n\n      Insert value into :class:`JSONField`. Will not overwrite existing.\n\n      Uses the `json_insert() <http://sqlite.org/json1.html#jset>`_ function\n      from the json1 extension.\n\n   .. method:: append(value, as_json=None)\n\n      :param value: a scalar value, list, or dictionary.\n      :param bool as_json: force the value to be treated as JSON, in which\n          case it will be serialized as JSON in Python beforehand. By\n          default, lists and dictionaries are treated as JSON to be\n          serialized, while strings and integers are passed as-is.\n\n      Append to the array stored in a :class:`JSONField`.\n\n      Uses the `json_set() <http://sqlite.org/json1.html#jset>`_ function\n      from the json1 extension.\n\n   .. method:: update(data)\n\n      :param data: a scalar value, list or dictionary to merge with the data\n          currently stored in a :class:`JSONField`. To remove a particular\n          key, set that key to ``None`` in the updated data.\n\n      Merge new data into the JSON value using the RFC-7396 MergePatch\n      algorithm to apply a patch (``data`` parameter) against the column\n      data. MergePatch can add, modify, or delete elements of a JSON object,\n      which means :meth:`~JSONField.update` is a generalized replacement\n      for both :meth:`~JSONField.set` and :meth:`~JSONField.remove`.\n      MergePatch treats JSON array objects as atomic, so ``update()`` cannot\n      append to an array, nor modify individual elements of an array.\n\n      For more information as well as examples, see the SQLite `json_patch() <http://sqlite.org/json1.html#jpatch>`_\n      function documentation.\n\n   .. method:: remove()\n\n      Remove the data stored in the :class:`JSONField`.\n\n      Uses the `json_remove <https://www.sqlite.org/json1.html#jrm>`_ function\n      from the json1 extension.\n\n   .. method:: json_type()\n\n      Return a string identifying the type of value stored in the column.\n\n      The type returned will be one of:\n\n      * object\n      * array\n      * integer\n      * real\n      * true\n      * false\n      * text\n      * null  <-- the string \"null\" means an actual NULL value\n      * NULL  <-- an actual NULL value means the path was not found\n\n      Uses the `json_type <https://www.sqlite.org/json1.html#jtype>`_\n      function from the json1 extension.\n\n   .. method:: length()\n\n      Return the length of the array stored in the column.\n\n      Uses the `json_array_length <https://www.sqlite.org/json1.html#jarraylen>`_\n      function from the json1 extension.\n\n   .. method:: children()\n\n      The ``children`` function corresponds to ``json_each``, a table-valued\n      function that walks the JSON value provided and returns the immediate\n      children of the top-level array or object. If a path is specified, then\n      that path is treated as the top-most element.\n\n      The rows returned by calls to ``children()`` have the following\n      attributes:\n\n      * ``key``: the key of the current element relative to its parent.\n      * ``value``: the value of the current element.\n      * ``type``: one of the data-types (see :meth:`~JSONField.json_type`).\n      * ``atom``: the scalar value for primitive types, ``NULL`` for arrays and objects.\n      * ``id``: a unique ID referencing the current node in the tree.\n      * ``parent``: the ID of the containing node.\n      * ``fullkey``: the full path describing the current element.\n      * ``path``: the path to the container of the current row.\n\n      Internally this method uses the `json_each <https://www.sqlite.org/json1.html#jeach>`_\n      (documentation link) function from the json1 extension.\n\n      Example usage (compare to :meth:`~JSONField.tree` method):\n\n      .. code-block:: python\n\n          class KeyData(Model):\n              key = TextField()\n              data = JSONField()\n\n          KeyData.create(key='a', data={'k1': 'v1', 'x1': {'y1': 'z1'}})\n          KeyData.create(key='b', data={'x1': {'y1': 'z1', 'y2': 'z2'}})\n\n          # We will query the KeyData model for the key and all the\n          # top-level keys and values in it's data field.\n          kd = KeyData.data.children().alias('children')\n          query = (KeyData\n                   .select(kd.c.key, kd.c.value, kd.c.fullkey)\n                   .from_(KeyData, kd)\n                   .order_by(kd.c.key)\n                   .tuples())\n          print(query[:])\n\n          # PRINTS:\n          [('a', 'k1', 'v1',                    '$.k1'),\n           ('a', 'x1', '{\"y1\":\"z1\"}',           '$.x1'),\n           ('b', 'x1', '{\"y1\":\"z1\",\"y2\":\"z2\"}', '$.x1')]\n\n   .. method:: tree()\n\n      The ``tree`` function corresponds to ``json_tree``, a table-valued\n      function that recursively walks the JSON value provided and returns\n      information about the keys at each level. If a path is specified, then\n      that path is treated as the top-most element.\n\n      The rows returned by calls to ``tree()`` have the same attributes as\n      rows returned by calls to :meth:`~JSONField.children`:\n\n      * ``key``: the key of the current element relative to its parent.\n      * ``value``: the value of the current element.\n      * ``type``: one of the data-types (see :meth:`~JSONField.json_type`).\n      * ``atom``: the scalar value for primitive types, ``NULL`` for arrays and objects.\n      * ``id``: a unique ID referencing the current node in the tree.\n      * ``parent``: the ID of the containing node.\n      * ``fullkey``: the full path describing the current element.\n      * ``path``: the path to the container of the current row.\n\n      Internally this method uses the `json_tree <https://www.sqlite.org/json1.html#jtree>`_\n      (documentation link) function from the json1 extension.\n\n      Example usage:\n\n      .. code-block:: python\n\n          class KeyData(Model):\n              key = TextField()\n              data = JSONField()\n\n          KeyData.create(key='a', data={'k1': 'v1', 'x1': {'y1': 'z1'}})\n          KeyData.create(key='b', data={'x1': {'y1': 'z1', 'y2': 'z2'}})\n\n          # We will query the KeyData model for the key and all the\n          # keys and values in it's data field, recursively.\n          kd = KeyData.data.tree().alias('tree')\n          query = (KeyData\n                   .select(kd.c.key, kd.c.value, kd.c.fullkey)\n                   .from_(KeyData, kd)\n                   .order_by(kd.c.key)\n                   .tuples())\n          print(query[:])\n\n          # PRINTS:\n          [('a',  None,  '{\"k1\":\"v1\",\"x1\":{\"y1\":\"z1\"}}', '$'),\n           ('b',  None,  '{\"x1\":{\"y1\":\"z1\",\"y2\":\"z2\"}}', '$'),\n           ('a',  'k1',  'v1',                           '$.k1'),\n           ('a',  'x1',  '{\"y1\":\"z1\"}',                  '$.x1'),\n           ('b',  'x1',  '{\"y1\":\"z1\",\"y2\":\"z2\"}',        '$.x1'),\n           ('a',  'y1',  'z1',                           '$.x1.y1'),\n           ('b',  'y1',  'z1',                           '$.x1.y1'),\n           ('b',  'y2',  'z2',                           '$.x1.y2')]\n\n\n.. class:: JSONPath(field, path=None)\n\n   :param JSONField field: the field object we intend to access.\n   :param tuple path: Components comprising the JSON path.\n\n   A convenient, Pythonic way of representing JSON paths for use with\n   :class:`JSONField`. Implements the same methods as :class:`JSONField` but\n   designed for operating on nested items, e.g.:\n\n   .. code-block:: python\n\n      Config.create(data={'timeout': 30, 'retries': {'max': 5}})\n\n      # Both Config.data['timeout'] and Config.data['retries']['max']\n      # are instances of JSONPath:\n      query = (Config\n               .select(Config.data['timeout'])\n               .where(Config.data['retries']['max'] < 10))\n\n.. class:: JSONBField(json_dumps=None, json_loads=None, **kwargs)\n\n   Extends :class:`JSONField` and stores data in the binary ``jsonb`` format\n   (SQLite 3.45.0+). When reading raw column values the data is in its\n   encoded binary form use the :meth:`~JSONBField.json` method to decode:\n\n   .. code-block:: python\n\n      # Raw read returns binary:\n      kv = KV.get(KV.key == 'a')\n      kv.value   # b\"l'k1'v1\"\n\n      # Use .json() to get a Python object:\n      kv = KV.select(KV.value.json()).get()\n      kv.value   # {'k1': 'v1'}\n\n   .. method:: json()\n\n      Indicate the JSONB field-data should be deserialized and returned as\n      JSON (as opposed to the SQLite binary format).\n\n\n.. _sqlite-fts:\n\nFull-Text Search\n-----------------\n\nPeewee supports :ref:`FTS3, FTS4 <sqlite-fts4>` (legacy, widely available) and\n:ref:`FTS5 <sqlite-fts5>` full-text search extensions.\n\nThe general pattern is:\n\n1. Define a :class:`FTSModel` or :class:`FTS5Model` subclass with one or more\n   :class:`SearchField` columns.\n2. When a row is created or updated in the source table, insert or update\n   the corresponding row in the search index.\n3. Query the index using :meth:`~FTSModel.match` and rank results with\n   :meth:`~FTSModel.bm25` (or :meth:`~FTSModel.rank` for FTS5).\n\nConsult the SQLite documentation for FTS query syntax diagrams:\n\n* `FTS3 and FTS4 <https://www.sqlite.org/fts3.html#full_text_index_queries>`__\n* `FTS5 <https://sqlite.org/fts5.html#full_text_query_syntax>`__\n\n.. class:: SearchField(unindexed=False, column_name=None)\n\n   Field type for full-text search virtual tables. Raises an exception if\n   constraints (``null=False``, ``unique=True``, etc.) are specified, since\n   FTS tables do not support them.\n\n   Pass ``unindexed=True`` to store metadata alongside the search index\n   without indexing it:\n\n   .. code-block:: python\n\n      class DocumentIndex(FTSModel):\n          title = SearchField()\n          content = SearchField()\n          tags = SearchField()\n          timestamp = SearchField(unindexed=True)\n\n   .. method:: match(term)\n\n      :param str term: full-text search query/terms.\n      :return: a :class:`Expression` corresponding to the ``MATCH``\n          operator.\n\n      Sqlite's full-text search supports searching either the full table,\n      including all indexed columns, **or** searching individual columns. The\n      :meth:`~SearchField.match` method can be used to restrict search to\n      a single column:\n\n      .. code-block:: python\n\n         # Search *only* the title field and return results ordered by\n         # relevance, using bm25.\n         query = (DocumentIndex\n                  .select(DocumentIndex, DocumentIndex.bm25().alias('score'))\n                  .where(DocumentIndex.title.match('python'))\n                  .order_by(DocumentIndex.bm25()))\n\n      To search *all* indexed columns, use the :meth:`FTSModel.match` method:\n\n      .. code-block:: python\n         :emphasize-lines: 5\n\n         # Searches *both* the title and body and return results ordered by\n         # relevance, using bm25.\n         query = (DocumentIndex\n                  .select(DocumentIndex, DocumentIndex.bm25().alias('score'))\n                  .where(DocumentIndex.match('python'))\n                  .order_by(DocumentIndex.bm25()))\n\n   .. method:: highlight(left, right)\n\n      :param str left: opening tag for highlight, e.g. ``'<b>'``\n      :param str right: closing tag for highlight, e.g. ``'</b>'``\n\n      When performing a search using the ``MATCH`` operator, FTS5 can return\n      text highlighting matches in a given column.\n\n      .. code-block:: python\n\n         # Search for items matching string 'python' and return the title\n         # highlighted with square brackets.\n         query = (SearchIndex\n                  .search('python')\n                  .select(SearchIndex.title.highlight('[', ']').alias('hi')))\n\n         for result in query:\n             print(result.hi)\n\n         # For example, might print:\n         # Learn [python] the hard way\n\n   .. method:: snippet(left, right, over_length='...', max_tokens=16)\n\n      :param str left: opening tag for highlight, e.g. ``'<b>'``\n      :param str right: closing tag for highlight, e.g. ``'</b>'``\n      :param str over_length: text to prepend or append when snippet exceeds\n          the maximum number of tokens.\n      :param int max_tokens: max tokens returned, **must be 1 - 64**.\n\n      When performing a search using the ``MATCH`` operator, FTS5 can return\n      text with a snippet containing the highlighted match in a given column.\n\n      .. code-block:: python\n\n         # Search for items matching string 'python' and return the title\n         # highlighted with square brackets.\n         query = (SearchIndex\n                  .search('python')\n                  .select(SearchIndex.title.snippet('[', ']').alias('snip')))\n\n         for result in query:\n             print(result.snip)\n\n.. _sqlite-fts4:\n\nFTS4 / ``FTSModel``\n^^^^^^^^^^^^^^^^^^^\n\nFTSModel enables Peewee applications to store data in an efficient full-text\nsearch index using SQLite `FTS4 <https://www.sqlite.org/fts3.html>`_.\n\nFTSModel caveats:\n\n* All queries **except** ``MATCH`` and ``rowid`` lookup require a full table scan.\n* Constraints, foreign-keys, and indexes are not supported.\n* All columns are treated as ``TEXT``.\n* No built-in ranking. Peewee provides several implementations which can be\n  automatically registered by passing ``rank_functions=True`` to ``SqliteDatabase(...)``.\n* FTSModel ``rowid`` primary key may be declared using :class:`RowIDField`.\n  Lookups on the ``rowid`` are very efficient.\n\nGiven these constraints all fields besides ``rowid`` should be instances of\n:class:`SearchField` to ensure correctness.\n\n.. tip::\n   Because of the lack of secondary indexes, it usually makes sense to treat\n   the ``FTSModel.rowid`` primary key as a foreign-key to a row in a normal\n   SQLite table.\n\nExample:\n\n.. code-block:: python\n\n   from peewee import *\n   from playhouse.sqlite_ext import FTSModel, SearchField\n\n   db = SqliteDatabase('app.db', rank_functions=True)\n\n   class Document(Model):\n       # Canonical source of data, stored in a normal table.\n       author = ForeignKeyField(User, backref='documents')\n       title = TextField(null=False, unique=True)\n       content = TextField(null=False)\n       timestamp = DateTimeField()\n\n       class Meta:\n           database = db\n\n   class DocumentIndex(FTSModel):\n       # Full-text search index.\n       rowid = RowIDField()\n       title = SearchField()\n       content = SearchField()\n       author = SearchField(unindexed=True)\n\n       class Meta:\n           database = db\n           # Use the porter stemming algorithm to tokenize content, optimize\n           # prefix searches of 3 or 4 characters.\n           options = {'tokenize': 'porter unicode61', 'prefix': [3, 4]}\n\nStore data by inserting it into the FTS table:\n\n.. code-block:: python\n\n   # Store a document in the index:\n   DocumentIndex.create(\n       rowid=document.id,  # Set rowid to match Document's id.\n       title=document.title,\n       content=document.content,\n       author=document.author.get_full_name())\n\n   # Equivalent:\n   (DocumentIndex\n    .insert({\n        'rowid': document.id,\n        'title': document.title,\n        'content': document.content,\n        'author': document.author.get_full_name()})\n    .execute())\n\n:class:`FTSModel` provides several shortcuts for full-text search queries:\n\n.. code-block:: python\n\n   # Simple search using basic ranking algorithm.\n   results = DocumentIndex.search('python sqlite')\n\n   # BM25 search With score and per-column weighting:\n   results = DocumentIndex.search_bm25(\n       'python sqlite',\n       weights={'title': 2.0, 'content': 1.0},\n       with_score=True,\n       score_alias='relevance')\n\n   for r in results:\n       print(r.title, r.relevance)\n\nAn important method of searching relies on the ``rowid`` of the indexed\ndata matching the document's canonical id. Using this technique we can\napply additional filters and retrieve the matching ``Document`` objects\nefficiently:\n\n.. code-block:: python\n\n   # Search and ensure we only retrieve articles from the last 30 days.\n   cutoff = datetime.datetime.now() - datetime.timedelta(days=30)\n\n   query = (Document\n            .select()\n            .join(\n                DocumentIndex,\n                on=(Document.id == DocumentIndex.rowid))\n            .where(\n                (Document.timestamp >= cutoff) &\n                DocumentIndex.match('python sqlite'))\n            .order_by(DocumentIndex.bm25()))\n\n.. warning::\n   All SQL queries on ``FTSModel`` classes will be full-table scans\n   **except** full-text searches and ``rowid`` lookups.\n\n.. _sqlite-fts4-external-content:\n\n.. topic:: External Content\n\n   If the primary source of the content you are indexing exists in a separate\n   table, you can save some disk space by instructing SQLite to not store an\n   additional copy of the search index content.\n\n   To accomplish this, you can specify a table using the ``content`` option.\n   The `FTS4 documentation <https://www.sqlite.org/fts3.html#the_content_option_>`_\n   and `FTS5 documentation <https://www.sqlite.org/fts5.html#external_content_and_contentless_tables>`_\n   have more information.\n\n   Here is a short example illustrating how to implement this with peewee:\n\n   .. code-block:: python\n\n      class Blog(Model):\n          title = TextField()\n          pub_date = DateTimeField(default=datetime.datetime.now)\n          content = TextField()  # We want to search this.\n\n          class Meta:\n              database = db\n\n      class BlogIndex(FTSModel):  # or FTS5Model.\n          content = SearchField()\n\n          class Meta:\n              database = db\n              options = {\n                  'content': Blog,  # Data source.\n                  'content_rowid': Blog.id,  # FTS5 only.\n              }\n\n      db.create_tables([Blog, BlogIndex])\n\n      # Now, we can manage content in the BlogIndex. To populate the\n      # search index:\n      BlogIndex.rebuild()\n\n      # Optimize the index.\n      BlogIndex.optimize()\n\n   The ``content`` option accepts a :class:`Model` and can reduce the amount of\n   storage used by the database at the expense of requiring more care and\n   attention to keeping data synchronized.\n\n\n.. class:: FTSModel()\n\n   Base Model class suitable for working with SQLite FTS3 / FTS4.\n\n   Supports the following options:\n\n   * ``content``: :class:`Model` containing external content, or empty string\n     for \"contentless\"\n   * ``prefix``: integer(s). Ex: '2' or '2,3,4'\n   * ``tokenize``: simple, porter, unicode61. Ex: 'porter'\n\n   Example:\n\n   .. code-block:: python\n\n      class DocumentIndex(FTSModel):\n          title = SearchField()\n          body = SearchField()\n\n          class Meta:\n              database = db\n              options = {\n                  'tokenize': 'porter unicode61',\n                  'prefix': '3',\n              }\n\n   .. classmethod:: match(term)\n\n      :param term: Search term or expression. `FTS syntax documentation <https://www.sqlite.org/fts3.html#full_text_index_queries>`__.\n\n      Generate a SQL expression representing a search for the given term or\n      expression in the table. SQLite uses the ``MATCH`` operator to indicate\n      a full-text search.\n\n      Example:\n\n      .. code-block:: python\n\n         # Search index for \"search phrase\" and return results ranked\n         # by relevancy using the BM25 algorithm.\n         query = (DocumentIndex\n                  .select()\n                  .where(DocumentIndex.match('search phrase'))\n                  .order_by(DocumentIndex.bm25()))\n\n         for result in query:\n             print('Result: %s' % result.title)\n\n   .. classmethod:: search(term, weights=None, with_score=False, score_alias='score', explicit_ordering=False)\n\n      :param term: Search term or expression. `FTS syntax documentation <https://www.sqlite.org/fts3.html#full_text_index_queries>`__.\n      :param weights: A list of weights for the columns, ordered with respect\n        to the column's position in the table. **Or**, a dictionary keyed by\n        the field or field name and mapped to a value.\n      :param with_score: Whether the score should be returned as part of\n        the ``SELECT`` statement.\n      :param str score_alias: Alias to use for the calculated rank score.\n        This is the attribute you will use to access the score\n        if ``with_score=True``.\n      :param bool explicit_ordering: Order using full SQL function to\n          calculate rank, as opposed to simply referencing the score alias\n          in the ORDER BY clause.\n\n      Shorthand way of searching for a term and sorting results by the\n      quality of the match.\n\n      This method uses a simplified algorithm for determining the\n      relevance rank of results. For more sophisticated result ranking,\n      use the :meth:`~FTSModel.search_bm25` method.\n\n      .. code-block:: python\n\n         # Simple search.\n         docs = DocumentIndex.search('search term')\n         for result in docs:\n             print(result.title)\n\n         # More complete example.\n         docs = DocumentIndex.search(\n             'search term',\n             weights={'title': 2.0, 'content': 1.0},\n             with_score=True,\n             score_alias='search_score')\n         for result in docs:\n             print(result.title, result.search_score)\n\n   .. classmethod:: search_bm25(term, weights=None, with_score=False, score_alias='score', explicit_ordering=False)\n\n      :param term: Search term or expression. `FTS syntax documentation <https://www.sqlite.org/fts3.html#full_text_index_queries>`__.\n      :param weights: A list of weights for the columns, ordered with respect\n        to the column's position in the table. **Or**, a dictionary keyed by\n        the field or field name and mapped to a value.\n      :param with_score: Whether the score should be returned as part of\n        the ``SELECT`` statement.\n      :param str score_alias: Alias to use for the calculated rank score.\n        This is the attribute you will use to access the score\n        if ``with_score=True``.\n      :param bool explicit_ordering: Order using full SQL function to\n          calculate rank, as opposed to simply referencing the score alias\n          in the ORDER BY clause.\n\n      Shorthand way of searching for a term and sorting results by the\n      quality of the match using the BM25 algorithm.\n\n      .. attention::\n         The BM25 ranking algorithm is only available for FTS4. If you are\n         using FTS3, use the :meth:`~FTSModel.search` method instead.\n\n   .. classmethod:: search_bm25f(term, weights=None, with_score=False, score_alias='score', explicit_ordering=False)\n\n      Same as :meth:`FTSModel.search_bm25`, but using the BM25f variant\n      of the BM25 ranking algorithm.\n\n   .. classmethod:: search_lucene(term, weights=None, with_score=False, score_alias='score', explicit_ordering=False)\n\n      Same as :meth:`FTSModel.search_bm25`, but using the result ranking\n      algorithm from the Lucene search engine.\n\n   .. classmethod:: rank(col1_weight, col2_weight...coln_weight)\n\n      :param float col_weight: (Optional) weight to give to the *ith* column\n          of the model. By default all columns have a weight of ``1.0``.\n\n      Generate an expression that will calculate and return the quality of\n      the search match. This ``rank`` can be used to sort the search results.\n\n      The ``rank`` function accepts optional parameters that allow you to\n      specify weights for the various columns. If no weights are specified,\n      all columns are considered of equal importance.\n\n      The algorithm used by :meth:`~FTSModel.rank` is simple and\n      relatively quick. For more sophisticated result ranking, use:\n\n      * :meth:`~FTSModel.bm25`\n      * :meth:`~FTSModel.bm25f`\n      * :meth:`~FTSModel.lucene`\n\n      .. code-block:: python\n\n         query = (DocumentIndex\n                  .select(\n                      DocumentIndex,\n                      DocumentIndex.rank().alias('score'))\n                  .where(DocumentIndex.match('search phrase'))\n                  .order_by(DocumentIndex.rank()))\n\n         for search_result in query:\n             print(search_result.title, search_result.score)\n\n   .. classmethod:: bm25(col1_weight, col2_weight...coln_weight)\n\n      :param float col_weight: (Optional) weight to give to the *ith* column\n          of the model. By default all columns have a weight of ``1.0``.\n\n      Generate an expression that will calculate and return the quality of\n      the search match using the `BM25 algorithm <https://en.wikipedia.org/wiki/Okapi_BM25>`_.\n      This value can be used to sort the search results.\n\n      Like :meth:`~FTSModel.rank`, ``bm25`` function accepts optional\n      parameters that allow you to specify weights for the various columns.\n      If no weights are specified, all columns are considered of equal\n      importance.\n\n      The BM25 result ranking algorithm requires FTS4. If you are using\n      FTS3, use :meth:`~FTSModel.rank` instead.\n\n      .. code-block:: python\n\n         query = (DocumentIndex\n                  .select(\n                      DocumentIndex,\n                      DocumentIndex.bm25().alias('score'))\n                  .where(DocumentIndex.match('search phrase'))\n                  .order_by(DocumentIndex.bm25()))\n\n         for search_result in query:\n             print(search_result.title, search_result.score)\n\n      The above code example is equivalent to calling the\n      :meth:`~FTSModel.search_bm25` method:\n\n          .. code-block:: python\n\n             query = DocumentIndex.search_bm25('search phrase', with_score=True)\n             for search_result in query:\n                 print(search_result.title, search_result.score)\n\n   .. classmethod:: bm25f(col1_weight, col2_weight...coln_weight)\n\n      Identical to :meth:`~FTSModel.bm25`, except that it uses the BM25f\n      variant of the BM25 ranking algorithm.\n\n   .. classmethod:: lucene(col1_weight, col2_weight...coln_weight)\n\n      Identical to :meth:`~FTSModel.bm25`, except that it uses the Lucene\n      search result ranking algorithm.\n\n   .. classmethod:: rebuild()\n\n      Rebuild the search index. Only valid when the ``content`` option\n      was specified (content tables).\n\n   .. classmethod:: optimize()\n\n      Optimize the index.\n\n.. _sqlite-fts5:\n\nFTS5 / ``FTS5Model``\n^^^^^^^^^^^^^^^^^^^^\n\nFTS5Model enables Peewee applications to store data in an efficient full-text\nsearch index using SQLite `FTS5 <https://www.sqlite.org/fts5.html>`_. FTS5 also\ncomes with native BM25 result ranking.\n\nFTS5Model caveats:\n\n* All queries **except** ``MATCH`` and ``rowid`` lookup require a full table scan.\n* Constraints, foreign-keys, and indexes are not supported. All columns **must**\n  be instances of :class:`SearchField`.\n* FTS5Model ``rowid`` primary key may be declared using :class:`RowIDField`.\n  Lookups on the ``rowid`` are very efficient.\n\n.. tip::\n   Because of the lack of secondary indexes, it usually makes sense to treat\n   the ``FTS5Model.rowid`` primary key as a foreign-key to a row in a normal\n   SQLite table.\n\nExample:\n\n.. code-block:: python\n\n   from peewee import *\n   from playhouse.sqlite_ext import FTS5Model, SearchField\n\n   db = SqliteDatabase('app.db')\n\n   class Document(Model):\n       # Canonical source of data, stored in a normal table.\n       author = ForeignKeyField(User, backref='documents')\n       title = TextField(null=False, unique=True)\n       content = TextField(null=False)\n       timestamp = DateTimeField()\n\n       class Meta:\n           database = db\n\n   class DocumentIndex(FTS5Model):\n       # Full-text search index.\n       rowid = RowIDField()\n       title = SearchField()\n       content = SearchField()\n       author = SearchField(unindexed=True)\n\n       class Meta:\n           database = db\n           # Use the porter stemming algorithm and unicode tokenizers,\n           # and optimize prefix matches of 3 or 4 characters.\n           options = {'tokenize': 'porter unicode61', 'prefix': [3, 4]}\n\n   # Check that FTS5 is available:\n   if not DocumentIndex.fts5_installed():\n       raise RuntimeError('FTS5 is not available in this SQLite build.')\n\nStore data by inserting it into the FTS5 table:\n\n.. code-block:: python\n\n   # Store a document in the index:\n   DocumentIndex.create(\n       rowid=document.id,  # Set rowid to match Document's id.\n       title=document.title,\n       content=document.content,\n       author=document.author.get_full_name())\n\n   # Equivalent:\n   (DocumentIndex\n    .insert({\n        'rowid': document.id,\n        'title': document.title,\n        'content': document.content,\n        'author': document.author.get_full_name()})\n    .execute())\n\n:class:`FTS5Model` provides several shortcuts for full-text search queries:\n\n.. code-block:: python\n\n   # Simple search (BM25, ordered by relevance):\n   results = DocumentIndex.search('python sqlite')\n\n   # With score and per-column weighting:\n   results = DocumentIndex.search(\n       'python sqlite',\n       weights={'title': 2.0, 'content': 1.0},\n       with_score=True,\n       score_alias='relevance')\n\n   for r in results:\n       print(r.title, r.relevance)\n\n   # Highlight matches in the title:\n   for r in (DocumentIndex.search('python')\n             .select(DocumentIndex.title.highlight('[', ']').alias('hi'))):\n       print(r.hi)  # e.g. \"Learn [python] the hard way\"\n\n.. tip::\n   An important method of searching relies on the ``rowid`` of the indexed\n   data matching the document's canonical id. Using this technique we can\n   apply additional filters and retrieve the matching ``Document`` objects\n   efficiently:\n\n   .. code-block:: python\n\n      # Search and ensure we only retrieve articles from the last 30 days.\n      cutoff = datetime.datetime.now() - datetime.timedelta(days=30)\n\n      query = (Document\n               .select()\n               .join(\n                   DocumentIndex,\n                   on=(Document.id == DocumentIndex.rowid))\n               .where(\n                   (Document.timestamp >= cutoff) &\n                   DocumentIndex.match('python sqlite'))\n               .order_by(DocumentIndex.rank()))\n\nIf the primary source of the content you are indexing exists in a separate\ntable, you can save some disk space by instructing SQLite to not store an\nadditional copy of the search index content. See :ref:`External Content\n<sqlite-fts4-external-content>` for implementation details. The `FTS5 documentation <https://www.sqlite.org/fts5.html#external_content_and_contentless_tables>`_\nhas more information.\n\n.. class:: FTS5Model()\n\n   Inherits all :class:`FTSModel` methods plus.\n\n   Supports the following options:\n\n   * ``content``: :class:`Model` containing external content, or empty string\n     for \"contentless\"\n   * ``content_rowid``: :class:`Field` (external content primary key)\n   * ``prefix``: integer(s). Ex: '2' or ``[2, 3]``\n   * ``tokenize``: simple, porter, unicode61. Ex: 'porter unicode61'\n\n   Example:\n\n   .. code-block:: python\n\n      class DocumentIndex(FTS5Model):\n          title = SearchField()\n          body = SearchField()\n\n          class Meta:\n              database = db\n              options = {\n                  'tokenize': 'porter unicode61',\n                  'prefix': '3',\n              }\n\n   .. classmethod:: fts5_installed()\n\n      Return ``True`` if FTS5 is available.\n\n   .. classmethod:: match(term)\n\n      :param term: Search term or expression. `FTS5 syntax documentation <https://sqlite.org/fts5.html#full_text_query_syntax>`__.\n\n      Generate a SQL expression representing a search for the given term or\n      expression in the table. SQLite uses the ``MATCH`` operator to indicate\n      a full-text search.\n\n      Example:\n\n      .. code-block:: python\n\n         # Search index for \"search phrase\" and return results ranked\n         # by relevancy using the BM25 algorithm.\n         query = (DocumentIndex\n                  .select()\n                  .where(DocumentIndex.match('search phrase'))\n                  .order_by(DocumentIndex.rank()))\n\n         for result in query:\n             print('Result: %s' % result.title)\n\n   .. classmethod:: search(term, weights=None, with_score=False, score_alias='score')\n\n      :param term: Search term or expression. `FTS5 syntax documentation <https://sqlite.org/fts5.html#full_text_query_syntax>`__.\n      :param weights: A list of weights for the columns, ordered with respect\n        to the column's position in the table. **Or**, a dictionary keyed by\n        the field or field name and mapped to a value.\n      :param with_score: Whether the score should be returned as part of\n        the ``SELECT`` statement.\n      :param str score_alias: Alias to use for the calculated rank score.\n        This is the attribute you will use to access the score\n        if ``with_score=True``.\n      :param bool explicit_ordering: Order using full SQL function to\n          calculate rank, as opposed to simply referencing the score alias\n          in the ORDER BY clause.\n\n      Shorthand way of searching for a term and sorting results by the\n      quality of the match. The ``FTS5`` extension provides a built-in\n      implementation of the BM25 algorithm, which is used to rank the results\n      by relevance.\n\n      .. code-block:: python\n\n          # Simple search.\n          docs = DocumentIndex.search('search term')\n          for result in docs:\n              print(result.title)\n\n          # More complete example.\n          docs = DocumentIndex.search(\n              'search term',\n              weights={'title': 2.0, 'content': 1.0},\n              with_score=True,\n              score_alias='search_score')\n          for result in docs:\n              print(result.title, result.search_score)\n\n   .. classmethod:: search_bm25(term, weights=None, with_score=False, score_alias='score')\n\n      With FTS5, :meth:`~FTS5Model.search_bm25` is identical to the\n      :meth:`~FTS5Model.search` method.\n\n   .. classmethod:: rank(col1_weight, col2_weight...coln_weight)\n\n      :param float col_weight: (Optional) weight to give to the *ith* column\n          of the model. By default all columns have a weight of ``1.0``.\n\n      Generate an expression that will calculate and return the quality of\n      the search match using the `BM25 algorithm <https://en.wikipedia.org/wiki/Okapi_BM25>`_.\n      This value can be used to sort the search results.\n\n      The :meth:`~FTS5Model.rank` function accepts optional parameters\n      that allow you to specify weights for the various columns.  If no\n      weights are specified, all columns are considered of equal importance.\n\n      .. code-block:: python\n\n         query = (DocumentIndex\n                  .select(\n                      DocumentIndex,\n                      DocumentIndex.rank().alias('score'))\n                  .where(DocumentIndex.match('search phrase'))\n                  .order_by(DocumentIndex.rank()))\n\n         for search_result in query:\n             print(search_result.title, search_result.score)\n\n      The above code example is equivalent to calling the\n      :meth:`~FTS5Model.search` method:\n\n      .. code-block:: python\n\n         query = DocumentIndex.search('search phrase', with_score=True)\n         for search_result in query:\n             print(search_result.title, search_result.score)\n\n   .. classmethod:: bm25(col1_weight, col2_weight...coln_weight)\n\n      Because FTS5 provides built-in support for BM25, this method is identical\n      to :meth:`~FTS5Model.rank` method.\n\n   .. classmethod:: VocabModel(table_type='row'|'col'|'instance', table_name=None)\n\n      :param str table_type: Either 'row', 'col' or 'instance'.\n      :param table_name: Name for the vocab table. If not specified, will be\n          \"fts5tablename_v\".\n\n      Generate a model class suitable for accessing the `vocab table <http://sqlite.org/fts5.html#the_fts5vocab_virtual_table_module>`_\n      corresponding to FTS5 search index.\n\n   .. classmethod:: rebuild()\n\n      Rebuild the search index. Only valid when the ``content`` option\n      was specified (content tables).\n\n   .. classmethod:: optimize()\n\n      Optimize the index.\n\n\n.. _sqlite-udf:\n\nUser-Defined Function Collection\n---------------------------------\n\n.. module:: playhouse.sqlite_udf\n\nThe ``playhouse.sqlite_udf`` contains a number of functions, aggregates, and\ntable-valued functions grouped into named collections.\n\n.. code-block:: python\n\n   from playhouse.sqlite_udf import register_all, register_groups\n   from playhouse.sqlite_udf import DATE, STRING\n\n   db = SqliteDatabase('my_app.db')\n\n   register_all(db)                   # Register every function.\n   register_groups(db, DATE, STRING)  # Register selected groups.\n\n   # Register individual functions:\n   from playhouse.sqlite_udf import gzip, gunzip\n   db.register_function(gzip, 'gzip')\n   db.register_function(gunzip, 'gunzip')\n\nOnce registered, call functions via Peewee's ``fn`` namespace or raw SQL:\n\n.. code-block:: python\n\n   # Find most common URL hostnames.\n   query = (Link\n            .select(fn.hostname(Link.url).alias('host'), fn.COUNT(Link.id))\n            .group_by(fn.hostname(Link.url))\n            .order_by(fn.COUNT(Link.id).desc())\n            .tuples())\n\nAvailable functions\n^^^^^^^^^^^^^^^^^^^\n\n**CONTROL_FLOW**\n\n.. function:: if_then_else(cond, truthy, falsey=None)\n\n   Simple ternary-type operator, where, depending on the truthiness of the\n   ``cond`` parameter, either the ``truthy`` or ``falsey`` value will be\n   returned.\n\n**DATE**\n\n.. function:: strip_tz(date_str)\n\n   :param date_str: A datetime, encoded as a string.\n   :returns: The datetime with any timezone info stripped off.\n\n   The time is not adjusted in any way, the timezone is simply removed.\n\n.. function:: humandelta(nseconds, glue=', ')\n\n   :param int nseconds: Number of seconds, total, in timedelta.\n   :param str glue: Fragment to join values.\n   :returns: Easy-to-read description of timedelta.\n\n   Example, 86471 -> \"1 day, 1 minute, 11 seconds\"\n\n.. function:: mintdiff(datetime_value)\n\n   :param datetime_value: A date-time.\n   :returns: Minimum difference between any two values in list.\n\n   *Aggregate*: minimum difference between any two datetimes.\n\n.. function:: avgtdiff(datetime_value)\n\n   :param datetime_value: A date-time.\n   :returns: Average difference between values in list.\n\n   *Aggregate*: average difference between consecutive values.\n\n.. function:: duration(datetime_value)\n\n   :param datetime_value: A date-time.\n   :returns: Duration from smallest to largest value in list, in seconds.\n\n   *Aggregate*: duration from the smallest to the largest value, in seconds.\n\n.. function:: date_series(start, stop, step_seconds=86400)\n\n   :param datetime start: Start datetime\n   :param datetime stop: Stop datetime\n   :param int step_seconds: Number of seconds comprising a step.\n\n   *Table-value function*: returns rows consisting of the date/+time values\n   encountered iterating from start to stop, ``step_seconds`` at a time.\n\n   Additionally, if start does not have a time component and step_seconds is\n   greater-than-or-equal-to one day (86400 seconds), the values returned will\n   be dates. Conversely, if start does not have a date component, values will\n   be returned as times. Otherwise values are returned as datetimes.\n\n   Example:\n\n   .. code-block:: sql\n\n       SELECT * FROM date_series('2017-01-28', '2017-02-02');\n\n       value\n       -----\n       2017-01-28\n       2017-01-29\n       2017-01-30\n       2017-01-31\n       2017-02-01\n       2017-02-02\n\n**FILE**\n\n.. function:: file_ext(filename)\n\n   :param str filename: Filename to extract extension from.\n   :return: Returns the file extension, including the leading \".\".\n\n.. function:: file_read(filename)\n\n   :param str filename: Filename to read.\n   :return: Contents of the file.\n\n**HELPER**\n\n.. function:: gzip(data, compression=9)\n\n   :param bytes data: Data to compress.\n   :param int compression: Compression level (9 is max).\n   :returns: Compressed binary data.\n\n.. function:: gunzip(data)\n\n   :param bytes data: Compressed data.\n   :returns: Uncompressed binary data.\n\n.. function:: hostname(url)\n\n   :param str url: URL to extract hostname from.\n   :returns: hostname portion of URL\n\n.. function:: toggle(key)\n\n   :param key: Key to toggle.\n\n   Toggle a key between True/False state. Example:\n\n   .. code-block:: pycon\n\n      >>> toggle('my-key')\n      True\n      >>> toggle('my-key')\n      False\n      >>> toggle('my-key')\n      True\n\n.. function:: setting(key, value=None)\n\n   :param key: Key to set/retrieve.\n   :param value: Value to set.\n   :returns: Value associated with key.\n\n   Store/retrieve a setting in memory and persist during lifetime of\n   application. To get the current value, specify key. To set a new\n   value, call with key and new value.\n\n.. function:: clear_toggles()\n\n   Clears all state associated with the :func:`toggle` function.\n\n.. function:: clear_settings()\n\n   Clears all state associated with the :func:`setting` function.\n\n**MATH**\n\n.. function:: randomrange(start, stop=None, step=None)\n\n   :param int start: Start of range (inclusive)\n   :param int end: End of range(not inclusive)\n   :param int step: Interval at which to return a value.\n\n   Return a random integer between ``[start, end)``.\n\n.. function:: gauss_distribution(mean, sigma)\n\n   :param float mean: Mean value\n   :param float sigma: Standard deviation\n\n.. function:: sqrt(n)\n\n   Calculate the square root of ``n``.\n\n.. function:: tonumber(s)\n\n   :param str s: String to convert to number.\n   :returns: Integer, floating-point or NULL on failure.\n\n.. function:: mode(val)\n\n   :param val: Numbers in list.\n   :returns: The mode, or most-common, number observed.\n\n   *Aggregate*: calculates *mode* of values.\n\n.. function:: minrange(val)\n\n   :param val: Value\n   :returns: Min difference between two values.\n\n   *Aggregate*: minimum distance between two numbers in the sequence.\n\n.. function:: avgrange(val)\n\n   :param val: Value\n   :returns: Average difference between values.\n\n   *Aggregate*: average distance between consecutive numbers in the sequence.\n\n.. function:: range(val)\n\n   :param val: Value\n   :returns: The range from the smallest to largest value in sequence.\n\n   *Aggregate*: range of values observed.\n\n.. function:: median(val)\n\n   :param val: Value\n   :returns: The median, or middle, value in a sequence.\n\n   *Aggregate*: median value of a sequence.\n\n   .. note:: Only available if you compiled the ``_sqlite_udf`` extension.\n\n**STRING**\n\n.. function:: substr_count(haystack, needle)\n\n   Returns number of times ``needle`` appears in ``haystack``.\n\n.. function:: strip_chars(haystack, chars)\n\n   Strips any characters in ``chars`` from beginning and end of ``haystack``.\n\n.. function:: damerau_levenshtein_dist(s1, s2)\n\n   Computes the edit distance from s1 to s2 using the damerau variant of the\n   levenshtein algorithm.\n\n   .. note:: Only available if you compiled the ``_sqlite_udf`` extension.\n\n.. function:: levenshtein_dist(s1, s2)\n\n   Computes the edit distance from s1 to s2 using the levenshtein algorithm.\n\n   .. note:: Only available if you compiled the ``_sqlite_udf`` extension.\n\n.. function:: str_dist(s1, s2)\n\n   Computes the edit distance from s1 to s2 using the standard library\n   SequenceMatcher's algorithm.\n\n   .. note:: Only available if you compiled the ``_sqlite_udf`` extension.\n\n.. function:: regex_search(regex, search_string)\n\n   :param str regex: Regular expression\n   :param str search_string: String to search for instances of regex.\n\n   *Table-value function*: searches a string for substrings that match\n   the provided ``regex``. Returns rows for each match found.\n\n   Example:\n\n   .. code-block:: python\n\n      SELECT * FROM regex_search('\\w+', 'extract words, ignore! symbols');\n\n      value\n      -----\n      extract\n      words\n      ignore\n      symbols\n"
  },
  {
    "path": "docs/peewee/transactions.rst",
    "content": ".. _transactions:\n\nTransactions\n============\n\nA *transaction* groups one or more SQL statements into a single unit of work.\nEither all of the statements succeed and are committed to the database, or none\nof them are - the database rolls back to the state it was in before the\ntransaction began.\n\nPeewee operates in *autocommit mode*: every statement that runs outside an\nexplicit transaction runs in its own implicit transaction. To group statements,\nuse the tools described in this document.\n\ndb.atomic\n---------\n\n:meth:`Database.atomic` is the recommended transaction API. :meth:`~Database.atomic`\ncan be used as a context manager or a decorator, and it handles nesting\nautomatically.\n\nIf an unhandled exception occurs in a wrapped block, the current block will be\nrolled back. Otherwise the statements will be committed at the end of the block.\n\nAs a context manager:\n\n.. code-block:: python\n\n   with db.atomic() as txn:\n       user = User.create(username='charlie')\n       tweet = Tweet.create(user=user, content='Hello')\n\n   # Both rows are committed when block exits normally.\n\nAs a decorator:\n\n.. code-block:: python\n\n   @db.atomic()\n   def create_user_with_tweet(username, content):\n       user = User.create(username=username)\n       Tweet.create(user=user, content=content)\n       return user\n\nIf an unhandled exception propagates out of the block, the transaction (or\nsavepoint - see below) is rolled back and the exception continues to propagate:\n\n.. code-block:: python\n\n   with db.atomic() as txn:\n       User.create(username='huey')\n       # User has been INSERTed into the database but the transaction is not\n       # yet committed because we haven't left the scope of the \"with\" block.\n\n       raise ValueError('something went wrong')\n       # This exception is unhandled - the transaction will be rolled-back and\n       # the ValueError will be raised.\n\n   # User('huey') was NOT committed, the transaction rolled-back.\n   # The ValueError is raised here.\n\nManual Commit / Rollback\n------------------------\n\nYou can commit or roll-back explicitly inside an :meth:`~Database.atomic`\nblock. After calling :meth:`~Transaction.commit` or :meth:`~Transaction.rollback`\na new transaction (or savepoint) begins automatically:\n\n.. code-block:: python\n\n   with db.atomic() as txn:\n       try:\n           save_objects()\n       except SaveError:\n           txn.rollback()  # Roll back, new transaction starts automatically.\n           log_error()\n\n       finalize()  # Runs in a new transaction.\n\n   # finalize()'s changes are committed here.\n\nNesting Transactions\n--------------------\n\nThe outermost :meth:`~Database.atomic` block creates a transaction. Any nested\n``atomic()`` blocks create *savepoints* instead. A savepoint is a named point\nwithin a transaction to which you can roll back without affecting the rest of\nthe transaction.\n\n.. code-block:: python\n\n   with db.atomic():                        # Transaction begins.\n       User.create(username='charlie')\n\n       with db.atomic() as sp:              # Savepoint begins.\n           User.create(username='huey')\n           sp.rollback()                    # Rolls back huey only.\n           User.create(username='alice')    # New savepoint begins here.\n\n       User.create(username='mickey')\n   # Committed: charlie, alice, mickey. huey was rolled back.\n\nSavepoints can be nested arbitrarily deep:\n\n.. code-block:: python\n\n   with db.atomic():\n       with db.atomic():\n           with db.atomic() as inner:\n               do_something_risky()\n               inner.rollback()   # Only the innermost work is lost.\n           do_something_safe()\n\n``atomic()`` tracks the nesting depth internally. You do not need to\nmanage savepoint names or transaction state manually.\n\nExplicit Transaction\n--------------------\n\n:meth:`Database.transaction` opens an explicit transaction that does not\nnest. Any ``transaction()`` call inside an outer ``transaction()`` block is\nignored - only the outermost transaction is active.\n\nUse this only when you explicitly need a flat, non-nesting transaction. For\nmost cases, ``atomic()`` is the better choice.\n\nIf an exception occurs in a wrapped block, the transaction will be rolled back.\nOtherwise the statements will be committed at the end of the wrapped block.\n\n.. code-block:: python\n\n   with db.transaction() as txn:\n       User.create(username='mickey')\n       txn.commit()         # Commit now; a new transaction begins.\n       User.create(username='huey')\n       txn.rollback()       # Roll back huey; a new transaction begins.\n       User.create(username='zaizee')\n   # zaizee is committed when the block exits.\n\nIf you attempt to nest transactions with peewee using the\n:meth:`~Database.transaction` context manager, only the outer-most\ntransaction will be used.\n\nAs this may lead to unpredictable behavior, it is recommended that\nyou use :meth:`~Database.atomic`.\n\nExplicit Savepoints\n-------------------\n\n:meth:`Database.savepoint` creates a savepoint within an active transaction.\nSavepoints must occur within a transaction, but can be nested arbitrarily deep.\n\n.. code-block:: python\n\n   with db.transaction() as txn:\n       with db.savepoint() as sp:\n           User.create(username='mickey')\n\n       with db.savepoint() as sp2:\n           User.create(username='zaizee')\n           sp2.rollback()  # \"zaizee\" is not saved.\n           User.create(username='huey')\n\n   # mickey and huey were created.\n\nIf you manually commit or roll back a savepoint, a new savepoint will\nautomatically begin.\n\nAutocommit Mode\n---------------\n\nPeewee requires the underlying driver to run in autocommit mode and manages\ntransaction boundaries itself. This differs from the DB-API 2.0 default, which\nstarts a transaction implicitly and requires you to commit manually. As a\nresult, Peewee puts all DB-API drivers into *autocommit* mode.\n\nIn rare cases where you need to take direct control of ``BEGIN``/``COMMIT``/\n``ROLLBACK`` - bypassing Peewee's transaction management entirely - use\n:meth:`~Database.manual_commit`:\n\n.. code-block:: python\n\n   with db.manual_commit():\n       db.begin()  # Begin transaction explicitly.\n       try:\n           user.delete_instance(recursive=True)\n       except:\n           db.rollback()  # Rollback! An error occurred.\n           raise\n       else:\n           try:\n               db.commit()  # Commit changes.\n           except:\n               db.rollback()\n               raise\n\n``manual_commit`` suspends Peewee's transaction management for the duration\nof the block. ``atomic()`` and ``transaction()`` have no effect inside it.\nThis should rarely be needed in application code.\n\n.. _sqlite-locking:\n\nSQLite Transaction Locking Modes\n----------------------------------\n\nSQLite supports three locking modes for transactions. Use these when precise\ncontrol over read-write locking is required:\n\n.. code-block:: python\n\n   with db.atomic('EXCLUSIVE'):\n       # No other connection can read or write until this commits.\n       do_something()\n\n   @db.atomic('IMMEDIATE')\n   def load_data():\n       # No other writer is allowed, but readers can proceed.\n       insert_records()\n\nThe three modes:\n\n* **DEFERRED** (default) - acquires the minimum necessary lock as reads and\n  writes occur. Another writer can intervene between BEGIN and your first write.\n* **IMMEDIATE** - acquires a write reservation lock at BEGIN. Other writers are\n  blocked; readers can proceed.\n* **EXCLUSIVE** - acquires an exclusive lock at BEGIN. No other connection can\n  read or write until the transaction completes.\n\n.. seealso::\n   `SQLite locking documentation\n   <https://sqlite.org/lockingv3.html>`_.\n\n.. _postgres-isolation:\n\nPostgresql Isolation Notes\n--------------------------\n\nPostgresql supports configurable isolation levels per-transaction, from least\nto most strict:\n\n* READ UNCOMMITTED\n* READ COMMITTED (default in most deployments)\n* REPEATABLE READ\n* SERIALIZABLE\n\nSee the `Postgresql transaction isolation docs <https://www.postgresql.org/docs/current/transaction-iso.html>`__\nfor discussion.\n\nThe default isolation level is specified when initializing :class:`PostgresqlDatabase`:\n\n.. code-block:: python\n\n   db = PostgresqlDatabase(\n       'my_app',\n       user='postgres',\n       host='10.8.0.1',\n       port=5432,\n       isolation_level='SERIALIZABLE')\n\n   # Or use the constants provided by the driver.\n\n   from psycopg2.extensions import ISOLATION_LEVEL_SERIALIZABLE\n   db = PostgresqlDatabase(\n       ...\n       isolation_level=ISOLATION_LEVEL_SERIALIZABLE)\n\n\n   from psycopg import IsolationLevel\n   db = PostgresqlDatabase(\n       ...\n       isolation_level=IsolationLevel.SERIALIZABLE)\n\nTo control the isolation-level for a transaction, you can pass the desired\nsetting to the outer-most ``atomic()`` block:\n\n.. code-block:: python\n\n   with db.atomic('SERIALIZABLE') as txn:\n       ...\n\nIsolation level cannot be specified for nested ``atomic()`` blocks.\n"
  },
  {
    "path": "docs/peewee/writing.rst",
    "content": ".. _writing:\n\nWriting Data\n============\n\nThis document covers INSERT, UPDATE, and DELETE queries. Reading data is\ncovered in :ref:`querying`.\n\nAll examples use the canonical schema from :ref:`models`:\n\n.. code-block:: python\n\n   import datetime\n   from peewee import *\n\n   db = SqliteDatabase(':memory:')\n\n   class BaseModel(Model):\n       class Meta:\n           database = db\n\n   class User(BaseModel):\n       username = TextField(unique=True)\n\n   class Tweet(BaseModel):\n       user = ForeignKeyField(User, backref='tweets')\n       content = TextField()\n       timestamp = DateTimeField(default=datetime.datetime.now)\n       is_published = BooleanField(default=True)\n\nMethods which will be discussed:\n\n+-----------------+-----------------------------------------------------------+\n| Query           | Methods                                                   |\n+=================+===========================================================+\n| INSERT          | * :meth:`Model.create`                                    |\n|                 | * :meth:`Model.insert`                                    |\n|                 | * :meth:`Model.insert_many`                               |\n|                 | * :meth:`Model.insert_from`                               |\n|                 | * :meth:`Model.replace` (Postgres unsupported)            |\n|                 | * :meth:`Model.replace_many` (Postgres unsupported)       |\n+-----------------+-----------------------------------------------------------+\n| UPDATE          | * :meth:`Model.save`                                      |\n|                 | * :meth:`Model.update`                                    |\n+-----------------+-----------------------------------------------------------+\n| DELETE          | * :meth:`Model.delete_instance`                           |\n|                 | * :meth:`Model.delete`                                    |\n+-----------------+-----------------------------------------------------------+\n\n.. seealso:: :ref:`Extensive library of SQL / Peewee examples <query-library>`\n\n.. _inserting-records:\n\nInserting Records\n-----------------\n\nCreating a single row\n^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`~Model.create` inserts a row and returns the saved model instance:\n\n.. code-block:: pycon\n\n   >>> charlie = User.create(username='charlie')\n   >>> charlie.id\n   1\n\nThis will INSERT a new row into the database. The primary key will\nautomatically be retrieved and stored on the model instance.\n\nAlternatively, instantiate the model and call :meth:`~Model.save`. The\nfirst call to ``save()`` on a new instance performs an INSERT:\n\n.. code-block:: pycon\n\n   >>> user = User(username='huey')\n   >>> user.save()\n   1  # Returns number of rows modified.\n   >>> user.id\n   2\n\nAfter the first save, the model instance holds its primary key. Any subsequent\ncall to ``save()`` performs an UPDATE instead:\n\n.. code-block:: pycon\n\n   >>> user.username = 'Huey'\n   >>> user.save()\n   1  # Returns number of rows updated.\n\nFor a foreign key field, pass either the related model instance or its raw\nprimary key value:\n\n.. code-block:: python\n\n   Tweet.create(user=huey, content='Hello!')\n   Tweet.create(user=2, content='Also valid.')\n\nTo insert without constructing a model instance, use :meth:`~Model.insert`.\nIt returns the primary key of the new row:\n\n.. code-block:: pycon\n\n   >>> User.insert(username='mickey').execute()\n   3\n\n.. _bulk-inserts:\n\nBulk Inserts\n------------\n\nCalling :meth:`Model.create` or :meth:`Model.save` in a loop should be avoided:\n\n.. code-block:: python\n\n   data = [\n       {'username': 'alice'},\n       {'username': 'bob'},\n       {'username': 'carol'},\n   ]\n\n   for data_dict in data:\n       User.create(**data_dict)\n\nThe above is slow:\n\n1. **Does not wrap the loop in a transaction.** Result is each\n   :meth:`~Model.create` happens in its own :ref:`transaction <transactions>`.\n2. **Python interpreter** is getting in the way, and each :class:`Insert`\n   must be generated and parsed into SQL.\n3. **Large amount of data** (in terms of raw bytes of SQL) may be sent to the\n   database to parse.\n4. **Retrieving the last insert id**, which may not be necessary.\n\nYou can get a significant speedup by simply wrapping this in a transaction with\n:meth:`~Database.atomic`:\n\n.. code-block:: python\n   :emphasize-lines: 1\n\n   with db.atomic():\n       for data_dict in data:\n           User.create(**data_dict)\n\nThe fastest way to insert many rows is :meth:`~Model.insert_many`. It\naccepts a list of dicts or tuples and emits a single multi-row INSERT:\n\n.. code-block:: python\n\n   data = [\n       {'username': 'alice'},\n       {'username': 'bob'},\n       {'username': 'carol'},\n   ]\n   User.insert_many(data).execute()\n\n   # Tuples require an explicit field list:\n   data = [('alice',), ('bob',), ('carol',)]\n   User.insert_many(data, fields=[User.username]).execute()\n\nOptionally wrap the bulk insert in a transaction:\n\n.. code-block:: python\n\n   with db.atomic():\n       User.insert_many(data, fields=fields).execute()\n\nInsert queries support :meth:`~WriteQuery.returning` with Postgresql and SQLite\nto obtain the inserted rows:\n\n.. code-block:: python\n\n   query = (User\n            .insert_many([{'username': 'alice'}, {'username': 'bob'}])\n            .returning(User))\n   for user in query:\n       print(f'Added {user.username} with id = {user.id}')\n\nBatching large data sets\n^^^^^^^^^^^^^^^^^^^^^^^^\n\nDepending on the number of rows in your data source, you may need to break it\nup into chunks. SQLite in particular may have a `limit of 32766 <https://www.sqlite.org/limits.html#max_variable_number>`_\nvariables-per-query (batch size would then be 32766 // row length).\n\nYou can write a loop to batch your data into chunks. It is **strongly recommended**\nyou use a :ref:`transaction <transactions>`:\n\n.. code-block:: python\n\n   from peewee import chunked\n\n   with db.atomic():\n       for batch in chunked(data, 100):\n           User.insert_many(batch).execute()\n\n:func:`chunked` works on any iterable, including generators.\n\nBulk-creating model instances\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`~Model.bulk_create` accepts a list of unsaved model instances and\ninserts them efficiently. Pass ``batch_size`` to avoid hitting database limits:\n\n.. code-block:: python\n\n   users = [User(username=f'user_{i}') for i in range(1000)]\n   with db.atomic():\n       User.bulk_create(users, batch_size=100)\n\nIf you are using Postgresql (which supports the ``RETURNING`` clause), then\nthe previously-unsaved model instances will have their new primary key\nvalues automatically populated. Other backends will not.\n\nLoading from another table\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`~Model.insert_from` generates an ``INSERT INTO ... SELECT`` query,\ncopying rows from one table into another without round-tripping data through\nPython:\n\n.. code-block:: python\n\n   res = (TweetArchive\n          .insert_from(\n              Tweet.select(Tweet.user, Tweet.message),\n              fields=[TweetArchive.user, TweetArchive.message])\n          .execute())\n\nThe above query is equivalent to the following SQL:\n\n.. code-block:: sql\n\n   INSERT INTO \"tweet_archive\" (\"user_id\", \"message\")\n   SELECT \"user_id\", \"message\" FROM \"tweet\";\n\n.. _updating-records:\n\nUpdating Records\n----------------\n\nUpdating a model instance\n^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nModify attributes on a fetched instance and call :meth:`~Model.save` to\npersist the changes:\n\n.. code-block:: python\n\n   charlie = User.get(User.username == 'charlie')\n   charlie.username = 'charlie_admin'\n   charlie.save()   # Issues UPDATE WHERE id = charlie.id\n\nBy default, ``save()`` re-saves all fields. To only emit changed fields, set\n``only_save_dirty = True`` in the model's ``Meta``, or pass only the fields\nyou want to update:\n\n.. code-block:: python\n\n   charlie.username = 'charlie_v2'\n   charlie.save(only=[User.username])\n\nIf a model instance does not have a primary key, the first call to\n:meth:`~Model.save` will perform an INSERT query.\n\nOnce a model instance has a primary key, subsequent calls to :meth:`~Model.save`\nresult in an *UPDATE*.\n\nUpdating multiple rows\n^^^^^^^^^^^^^^^^^^^^^^\n\n:meth:`~Model.update` issues a single UPDATE that affects every row\nmatching the WHERE clause:\n\n.. code-block:: python\n\n   # Publish all unpublished tweets older than one week.\n   one_week_ago = datetime.datetime.now() - datetime.timedelta(days=7)\n\n   nrows = (Tweet\n            .update(is_published=True)\n            .where(\n                (Tweet.is_published == False) &\n                (Tweet.timestamp < one_week_ago))\n            .execute())\n\nThe return value is the number of rows affected.\n\nUpdate queries support :meth:`~WriteQuery.returning` with Postgresql and SQLite\nto obtain the updated rows:\n\n.. code-block:: python\n\n   query = (User\n            .update(spam=True)\n            .where(User.username.contains('billing'))\n            .returning(User))\n   for user in query:\n       print(f'Marked {user.username} as spam')\n\nBecause UPDATE queries do not support joins, we can use subqueries to update\nrows based on values in related tables. For example, unpublish all tweets by\nusers with ``'billing'`` in their username:\n\n.. code-block:: python\n\n   spammers = User.select().where(User.username.contains('billing'))\n\n   (Tweet\n    .update(is_published=False)\n    .where(Tweet.user.in_(spammers))\n    .execute())\n\nAtomic updates\n^^^^^^^^^^^^^^\n\nUse column expressions in ``update()`` to modify values without a read-modify-write\ncycle. Performing updates atomically prevents race-conditions:\n\n.. code-block:: python\n\n   # WRONG: reads each row into Python, increments, then saves.\n   # Vulnerable to race conditions; slow on many rows.\n   for stat in Stat.select().where(Stat.url == url):\n       stat.counter += 1\n       stat.save()\n\n   # CORRECT: single UPDATE statement, atomic at the database level.\n   Stat.update(counter=Stat.counter + 1).where(Stat.url == url).execute()\n\nAny SQL expression is valid on the right-hand side:\n\n.. code-block:: python\n\n   # Give every employee a 10% salary bonus added to their existing bonus.\n   Employee.update(bonus=Employee.bonus + (Employee.salary * 0.10)).execute()\n\n   # Denormalize a count column from a subquery.\n   tweet_count = (Tweet\n                  .select(fn.COUNT(Tweet.id))\n                  .where(Tweet.user == User.id))\n   User.update(num_tweets=tweet_count).execute()\n\nBulk-updating model instances\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWhen you have a list of modified model instances and want to update specific\nfields across all of them in one query, use :meth:`~Model.bulk_update`:\n\n.. code-block:: python\n\n   u1, u2, u3 = User.select().limit(3)\n   u1.username = 'u1-new'\n   u2.username = 'u2-new'\n   u3.username = 'u3-new'\n\n   User.bulk_update([u1, u2, u3], fields=[User.username])\n\nThis emits a single UPDATE using a SQL ``CASE`` expression. For large lists,\nspecify a ``batch_size`` and wrap in a transaction:\n\n.. code-block:: python\n\n   with db.atomic():\n       User.bulk_update(users, fields=[User.username], batch_size=50)\n\n``bulk_update`` may be slower than a direct UPDATE query when the list is\nvery large, because the generated ``CASE`` expression grows proportionally.\nFor updates that can be expressed as a single WHERE clause, the direct\n:meth:`~Model.update` approach is faster.\n\n.. _upsert:\n\nUpsert\n------\n\nAn *upsert* (INSERT or UPDATE) inserts a new row, or if a unique constraint\nwould be violated, updates the existing row instead.\n\nPeewee provides two complementary approaches.\n\n``on_conflict_replace`` - SQLite and MySQL\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSQLite and MySQL support a ``REPLACE`` query, which will replace the row in the\nevent of a conflict:\n\n.. code-block:: python\n\n   class User(BaseModel):\n       username = TextField(unique=True)\n       last_login = DateTimeField(null=True)\n\n   # Insert, or replace the entire existing row.\n   User.replace(username='huey', last_login=datetime.datetime.now()).execute()\n\n   # Equivalent using insert():\n   (User\n    .insert(username='huey', last_login=datetime.datetime.now())\n    .on_conflict_replace()\n    .execute())\n\n``replace`` deletes and re-inserts, which changes the primary key. Use\n``on_conflict`` (below) when the primary key must be preserved, or when\nonly some columns should be updated.\n\n``on_conflict`` - all backends\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe :meth:`~Insert.on_conflict` method is much more powerful.\n\n.. code-block:: python\n\n   class User(BaseModel):\n       username = TextField(unique=True)\n       last_login = DateTimeField(null=True)\n       login_count = IntegerField(default=0)\n\n   now = datetime.datetime.now()\n\n   (User\n    .insert(username='huey', last_login=now, login_count=1)\n    .on_conflict(\n        # Postgresql and SQLite require identifying the conflicting constraint.\n        # MySQL does not need this.\n        conflict_target=[User.username],\n\n        # Columns whose values should come from the incoming row:\n        preserve=[User.last_login],\n\n        # Columns to update using an expression:\n        update={User.login_count: User.login_count + 1})\n    .execute())\n\nCalling this query repeatedly will increment ``login_count`` atomically and\nupdate ``last_login`` on each call, without creating duplicate rows.\n\nThe ``EXCLUDED`` namespace references the values that would have been inserted\nif the constraint had not fired. This allows conditional updates:\n\n.. code-block:: python\n\n   class KV(BaseModel):\n       key = TextField(unique=True)\n       value = IntegerField()\n\n   KV.create(key='k1', value=1)\n\n   # Demonstrate usage of EXCLUDED.\n   # Here we will attempt to insert a new value for a given key. If that\n   # key already exists, then we will update its value with the *sum* of its\n   # original value and the value we attempted to insert -- provided that\n   # the new value is larger than the original value.\n   query = (KV.insert(key='k1', value=10)\n            .on_conflict(conflict_target=[KV.key],\n                         update={KV.value: KV.value + EXCLUDED.value},\n                         where=(EXCLUDED.value > KV.value)))\n\n   # Executing the above query will result in the following data being\n   # present in the \"kv\" table:\n   # (key='k1', value=11)\n   query.execute()\n\n   # If we attempted to execute the query *again*, then nothing would be\n   # updated, as the new value (10) is now less than the value in the\n   # original row (11).\n\nThere are several important concepts to understand when using ``ON CONFLICT``:\n\n* ``conflict_target=``: which column(s) have the UNIQUE constraint. For a user\n  table, this might be the user's email (SQLite and Postgresql only).\n* ``preserve=``: if a conflict occurs, this parameter is used to indicate which\n  values from the **new** data we wish to update.\n* ``update=``: if a conflict occurs, this is a mapping of data to apply to the\n  pre-existing row.\n* ``EXCLUDED``: this \"magic\" namespace allows you to reference the new data\n  that would have been inserted if the constraint hadn't failed.\n\nFull example:\n\n.. code-block:: python\n\n   class User(Model):\n       email = CharField(unique=True)  # Unique identifier for user.\n       last_login = DateTimeField()\n       login_count = IntegerField(default=0)\n       ip_log = TextField(default='')\n\n\n   # Demonstrates the above 4 concepts.\n   def login(email, ip):\n       rowid = (User\n                .insert({User.email: email,\n                         User.last_login: datetime.now(),\n                         User.login_count: 1,\n                         User.ip_log: ip})\n                .on_conflict(\n                    # If the INSERT fails due to a constraint violation on the\n                    # user email, then perform an UPDATE instead.\n                    conflict_target=[User.email],\n\n                    # Set the \"last_login\" to the value we would have inserted\n                    # (our call to datetime.now()).\n                    preserve=[User.last_login],\n\n                    # Increment the user's login count and prepend the new IP\n                    # to the user's ip history.\n                    update={User.login_count: User.login_count + 1,\n                            User.ip_log: fn.CONCAT(EXCLUDED.ip_log, ',', User.ip_log)})\n                .execute())\n\n       return rowid\n\n   # This will insert the initial row, returning the new row id (1).\n   print(login('test@example.com', '127.1'))\n\n   # Because test@example.com exists, this will trigger the UPSERT. The row id\n   # from above is returned again (1).\n   print(login('test@example.com', '127.2'))\n\n   u = User.get()\n   print(u.login_count, u.ip_log)\n\n   # Prints \"2 127.2,127.1\"\n\n.. seealso:: :meth:`Insert.on_conflict` and :class:`OnConflict`.\n\n``on_conflict_ignore``\n^^^^^^^^^^^^^^^^^^^^^^\n\nInsert the row, and silently do nothing if a constraint would be violated:\n\n.. code-block:: python\n\n   # Insert if username does not exist; ignore if it does.\n   User.insert(username='huey').on_conflict_ignore().execute()\n\nSupported by SQLite, MySQL, and Postgresql.\n\n.. _deleting-records:\n\nDeleting Records\n----------------\n\nDelete a single fetched instance with :meth:`~Model.delete_instance`:\n\n.. code-block:: python\n\n   tweet = Tweet.get_by_id(42)\n   tweet.delete_instance()   # Returns number of rows deleted.\n\nTo delete a row along with all dependent rows (rows in other tables that\nreference it via foreign key), pass ``recursive=True``:\n\n.. code-block:: python\n\n   # Deletes the user and all their tweets, favorites, etc.\n   with db.atomic():\n       user.delete_instance(recursive=True)\n\n``recursive=True`` works by querying for dependent rows and deleting them\nfirst - it does not rely on ``ON DELETE CASCADE``. For large graphs of\nrelated data, this can be slow. Be sure to wrap calls in a\n:ref:`transaction <transactions>` and consider using database-level cascade\nconstraints on the foreign keys.\n\nTo delete an arbitrary set of rows without fetching them:\n\n.. code-block:: python\n\n   # Delete all unpublished tweets older than 30 days.\n   cutoff = datetime.datetime.now() - datetime.timedelta(days=30)\n   nrows = (Tweet\n            .delete()\n            .where(\n                (Tweet.is_published == False) &\n                (Tweet.timestamp < cutoff))\n            .execute())\n\nDelete queries support :meth:`~WriteQuery.returning` with Postgresql and SQLite\nto obtain the deleted rows:\n\n.. code-block:: python\n\n   query = (User\n            .delete()\n            .where(User.username.contains('billing'))\n            .returning(User))\n   for user in query:\n       print(f'Deleted: {user.username}')\n\nBecause DELETE queries do not support joins, we can use subqueries to delete\nrows based on values in related tables. For example, delete all tweets by users\nwith ``'billing'`` in their username:\n\n.. code-block:: python\n\n   spammers = User.select().where(User.username.contains('billing'))\n\n   (Tweet\n    .delete()\n    .where(Tweet.user.in_(spammers))\n    .execute())\n\n.. seealso::\n   * :meth:`Model.delete_instance`\n   * :meth:`Model.delete`\n   * :class:`Delete`\n\n.. _returning-clause:\n\nReturning Clause\n----------------\n\n:class:`PostgresqlDatabase` and :class:`SqliteDatabase` (3.35.0+) support a\n``RETURNING`` clause on ``UPDATE``, ``INSERT`` and ``DELETE`` queries.\nSpecifying a ``RETURNING`` clause allows you to iterate over the rows accessed\nby the query.\n\nBy default, the return values upon execution of the different queries are:\n\n* ``INSERT`` - auto-incrementing primary key value of the newly-inserted row.\n  When not using an auto-incrementing primary key, Postgres will return the new\n  row's primary key, but SQLite and MySQL will not.\n* ``UPDATE`` - number of rows modified\n* ``DELETE`` - number of rows deleted\n\nWhen a returning clause is used the return value upon executing a query will be\nan iterable cursor object, providing access to data that was inserted, updated\nor deleted by the query.\n\nFor example, let's say you have an :class:`Update` that deactivates all\nuser accounts whose registration has expired. After deactivating them, you want\nto send each user an email letting them know their account was deactivated.\nRather than writing two queries, a ``SELECT`` and an ``UPDATE``, you can do\nthis in a single ``UPDATE`` query with a ``RETURNING`` clause:\n\n.. code-block:: python\n\n   query = (User\n            .update(is_active=False)\n            .where(User.registration_expired == True)\n            .returning(User))\n\n   # Send an email to every user that was deactivated.\n   for deactivate_user in query.execute():\n       send_deactivation_email(deactivated_user.email)\n\n   query = (User\n            .delete()\n            .where(User.is_spam == True)\n            .returning(User.id))\n   for user in query.execute():\n       print(f'Deleted spam user id: {user.id}')\n\nThe ``RETURNING`` clause is available on:\n\n* :class:`Insert`\n* :class:`Update`\n* :class:`Delete`\n\nAs another example, let's add a user and set their creation-date to the\nserver-generated current timestamp. We'll create and retrieve the new user's\nID, Email and the creation timestamp in a single query:\n\n.. code-block:: python\n\n   query = (User\n            .insert(email='foo@bar.com', created=fn.now())\n            .returning(User))  # Shorthand for all columns on User.\n\n   # When using RETURNING, execute() returns a cursor.\n   cursor = query.execute()\n\n   # Get the user object we just inserted and log the data:\n   user = cursor[0]\n   logger.info('Created user %s (id=%s) at %s', user.email, user.id, user.created)\n\nBy default the cursor will return :class:`Model` instances, but you can\nspecify a different row type:\n\n.. code-block:: python\n\n   data = [{'name': 'charlie'}, {'name': 'huey'}, {'name': 'mickey'}]\n   query = (User\n            .insert_many(data)\n            .returning(User.id, User.username)\n            .dicts())\n\n   for new_user in query.execute():\n       print('Added user \"%s\", id=%s' % (new_user['username'], new_user['id']))\n\nJust as with :class:`Select` queries, you can specify various :ref:`result row types <row-types>`.\n"
  },
  {
    "path": "docs/requirements.txt",
    "content": "docutils<0.18\nsphinx-rtd-theme\n"
  },
  {
    "path": "examples/adjacency_list.py",
    "content": "from peewee import *\n\n\ndb = SqliteDatabase(':memory:')\n\nclass Node(Model):\n    name = TextField()\n    parent = ForeignKeyField('self', backref='children', null=True)\n\n    class Meta:\n        database = db\n\n    def __str__(self):\n        return self.name\n\n    def dump(self, _indent=0):\n        return ('  ' * _indent + self.name + '\\n' +\n                ''.join(child.dump(_indent + 1) for child in self.children))\n\ndb.create_tables([Node])\n\ntree = ('root', (\n    ('n1', (\n        ('c11', ()),\n        ('c12', ()))),\n    ('n2', (\n        ('c21', ()),\n        ('c22', (\n            ('g221', ()),\n            ('g222', ()))),\n        ('c23', ()),\n        ('c24', (\n            ('g241', ()),\n            ('g242', ()),\n            ('g243', ())))))))\nstack = [(None, tree)]\nwhile stack:\n    parent, (name, children) = stack.pop()\n    node = Node.create(name=name, parent=parent)\n    for child_tree in children:\n        stack.insert(0, (node, child_tree))\n\n# Now that we have created the stack, let's eagerly load 4 levels of children.\n# To show that it works, we'll turn on the query debugger so you can see which\n# queries are executed.\nimport logging; logger = logging.getLogger('peewee')\nlogger.addHandler(logging.StreamHandler())\nlogger.setLevel(logging.DEBUG)\n\nC = Node.alias('c')\nG = Node.alias('g')\nGG = Node.alias('gg')\nGGG = Node.alias('ggg')\n\nroots = Node.select().where(Node.parent.is_null())\npf = prefetch(roots, C, (G, C), (GG, G), (GGG, GG))\nfor root in pf:\n    print(root.dump())\n"
  },
  {
    "path": "examples/analytics/app.py",
    "content": "\"\"\"\nExample \"Analytics\" app. To start using this on your site, do the following:\n\n* Create a postgresql database:\n\n    createdb analytics\n\n* Create an account for each domain you intend to collect analytics for, e.g.\n\n    Account.create(domain='charlesleifer.com')\n\n* Update configuration values marked \"TODO\", e.g. DOMAIN.\n\n* Run this app using the WSGI server of your choice.\n\n* Using the appropriate account id, add a `<script>` tag to each site you want\n  to collect analytics data from. I place mine at the bottom of the <body>:\n\n    <script src=\"http://yourdomain.com/a.js?id=<your account id>\"></script>\n\nTake a look at `reports.py` for some interesting queries you can perform\non your pageview data.\n\"\"\"\nimport datetime\nimport os\nfrom urllib.parse import parse_qsl, urlparse\nimport binascii\n\nfrom flask import Flask, Response, abort, g, request\nfrom peewee import *\nfrom playhouse.postgres_ext import BinaryJSONField, PostgresqlExtDatabase\n\n# Analytics settings.\n# 1px gif.\nBEACON = binascii.unhexlify(\n    '47494638396101000100800000dbdfef00000021f90401000000002c00000000010001000002024401003b')\nDATABASE_NAME = 'analytics'\nDOMAIN = 'http://analytics.yourdomain.com'  # TODO: change me.\nJAVASCRIPT = \"\"\"(function(id){\n    var d=document,i=new Image,e=encodeURIComponent;\n    i.src='%s/a.gif?id='+id+'&url='+e(d.location.href)+'&ref='+e(d.referrer)+'&t='+e(d.title);\n    })(%s)\"\"\".replace('\\n', '')\n\n# Flask settings.\nDEBUG = bool(os.environ.get('DEBUG'))\nSECRET_KEY = 'secret - change me'  # TODO: change me.\n\napp = Flask(__name__)\napp.config.from_object(__name__)\n\ndatabase = PostgresqlExtDatabase(DATABASE_NAME, user='postgres')\n\n\nclass BaseModel(Model):\n    class Meta:\n        database = database\n\n\nclass Account(BaseModel):\n    domain = CharField()\n\n    def verify_url(self, url):\n        netloc = urlparse(url).netloc\n        url_domain = '.'.join(netloc.split('.')[-2:])  # Ignore subdomains.\n        return self.domain == url_domain\n\n\nclass PageView(BaseModel):\n    account = ForeignKeyField(Account, backref='pageviews')\n    url = TextField()\n    timestamp = DateTimeField(default=datetime.datetime.now)\n    title = TextField(default='')\n    ip = CharField(default='')\n    referrer = TextField(default='')\n    headers = BinaryJSONField()\n    params = BinaryJSONField()\n\n    @classmethod\n    def create_from_request(cls, account, request):\n        parsed = urlparse(request.args['url'])\n        params = dict(parse_qsl(parsed.query))\n\n        return PageView.create(\n            account=account,\n            url=parsed.path,\n            title=request.args.get('t') or '',\n            ip=request.headers.get('x-forwarded-for', request.remote_addr),\n            referrer=request.args.get('ref') or '',\n            headers=dict(request.headers),\n            params=params)\n\n\n@app.route('/a.gif')\ndef analyze():\n    # Make sure an account id and url were specified.\n    if not request.args.get('id') or not request.args.get('url'):\n        abort(404)\n\n    # Ensure the account id is valid.\n    try:\n        account = Account.get(Account.id == request.args['id'])\n    except Account.DoesNotExist:\n        abort(404)\n\n    # Ensure the account id matches the domain of the URL we wish to record.\n    if not account.verify_url(request.args['url']):\n        abort(403)\n\n    # Store the page-view data in the database.\n    PageView.create_from_request(account, request)\n\n    # Return a 1px gif.\n    response = Response(app.config['BEACON'], mimetype='image/gif')\n    response.headers['Cache-Control'] = 'private, no-cache'\n    return response\n\n\n@app.route('/a.js')\ndef script():\n    account_id = request.args.get('id')\n    if account_id:\n        return Response(\n            app.config['JAVASCRIPT'] % (app.config['DOMAIN'], account_id),\n            mimetype='text/javascript')\n    return Response('', mimetype='text/javascript')\n\n\n@app.errorhandler(404)\ndef not_found(e):\n    return Response('<h3>Not found.</h3>')\n\n# Request handlers -- these two hooks are provided by flask and we will use them\n# to create and tear down a database connection on each request.\n\n\n@app.before_request\ndef before_request():\n    g.db = database\n    g.db.connection()\n\n\n@app.after_request\ndef after_request(response):\n    g.db.close()\n    return response\n\n\nif __name__ == '__main__':\n    database.create_tables([Account, PageView], safe=True)\n    app.run(debug=True)\n"
  },
  {
    "path": "examples/analytics/reports.py",
    "content": "from peewee import *\n\nfrom app import Account, PageView\n\n\nDEFAULT_ACCOUNT_ID = 1\n\nclass Report(object):\n    def __init__(self, account_id=DEFAULT_ACCOUNT_ID):\n        self.account = Account.get(Account.id == account_id)\n        self.date_range = None\n\n    def get_query(self):\n        query = PageView.select().where(PageView.account == self.account)\n        if self.date_range:\n            query = query.where(PageView.timestamp.between(*self.date_range))\n        return query\n\n    def top_pages_by_time_period(self, interval='day'):\n        \"\"\"\n        Get a breakdown of top pages per interval, i.e.\n\n        day         url     count\n        2014-01-01  /blog/  11\n        2014-01-02  /blog/  14\n        2014-01-03  /blog/  9\n        \"\"\"\n        date_trunc = fn.date_trunc(interval, PageView.timestamp)\n        return (self.get_query()\n                .select(\n                    PageView.url,\n                    date_trunc.alias(interval),\n                    fn.Count(PageView.id).alias('count'))\n                .group_by(PageView.url, date_trunc)\n                .order_by(\n                    SQL(interval),\n                    SQL('count').desc(),\n                    PageView.url))\n\n    def cookies(self):\n        \"\"\"\n        Retrieve the cookies header from all the users who visited.\n        \"\"\"\n        return (self.get_query()\n                .select(PageView.ip, PageView.headers['Cookie'])\n                .where(PageView.headers['Cookie'].is_null(False))\n                .tuples())\n\n    def user_agents(self):\n        \"\"\"\n        Retrieve user-agents, sorted by most common to least common.\n        \"\"\"\n        return (self.get_query()\n                .select(\n                    PageView.headers['User-Agent'],\n                    fn.Count(PageView.id))\n                .group_by(PageView.headers['User-Agent'])\n                .order_by(fn.Count(PageView.id).desc())\n                .tuples())\n\n    def languages(self):\n        \"\"\"\n        Retrieve languages, sorted by most common to least common. The\n        Accept-Languages header sometimes looks weird, i.e.\n        \"en-US,en;q=0.8,is;q=0.6,da;q=0.4\" We will split on the first semi-\n        colon.\n        \"\"\"\n        language = PageView.headers['Accept-Language']\n        first_language = fn.SubStr(\n            language,  # String to slice.\n            1,  # Left index.\n            fn.StrPos(language, ';'))\n        return (self.get_query()\n                .select(first_language, fn.Count(PageView.id))\n                .group_by(first_language)\n                .order_by(fn.Count(PageView.id).desc())\n                .tuples())\n\n    def trail(self):\n        \"\"\"\n        Get all visitors by IP and then list the pages they visited in order.\n        \"\"\"\n        inner = (self.get_query()\n                 .select(PageView.ip, PageView.url)\n                 .order_by(PageView.timestamp))\n        return (PageView\n                .select(\n                    PageView.ip,\n                    fn.array_agg(PageView.url).alias('urls'))\n                .from_(inner.alias('t1'))\n                .group_by(PageView.ip))\n\n    def _referrer_clause(self, domain_only=True):\n        if domain_only:\n            return fn.SubString(Clause(\n                PageView.referrer, SQL('FROM'), '.*://([^/]*)'))\n        return PageView.referrer\n\n    def top_referrers(self, domain_only=True):\n        \"\"\"\n        What domains send us the most traffic?\n        \"\"\"\n        referrer = self._referrer_clause(domain_only)\n        return (self.get_query()\n                .select(referrer, fn.Count(PageView.id))\n                .group_by(referrer)\n                .order_by(fn.Count(PageView.id).desc())\n                .tuples())\n\n    def referrers_for_url(self, domain_only=True):\n        referrer = self._referrer_clause(domain_only)\n        return (self.get_query()\n                .select(PageView.url, referrer, fn.Count(PageView.id))\n                .group_by(PageView.url, referrer)\n                .order_by(PageView.url, fn.Count(PageView.id).desc())\n                .tuples())\n\n    def referrers_to_url(self, domain_only=True):\n        referrer = self._referrer_clause(domain_only)\n        return (self.get_query()\n                .select(referrer, PageView.url, fn.Count(PageView.id))\n                .group_by(referrer, PageView.url)\n                .order_by(referrer, fn.Count(PageView.id).desc())\n                .tuples())\n"
  },
  {
    "path": "examples/analytics/requirements.txt",
    "content": "peewee\nflask\npsycopg2\n"
  },
  {
    "path": "examples/analytics/run_example.py",
    "content": "#!/usr/bin/env python\n\nimport sys\nsys.path.insert(0, '../..')\n\nfrom app import app\napp.run(debug=True)\n"
  },
  {
    "path": "examples/anomaly_detection.py",
    "content": "import math\nfrom peewee import *\n\n\ndb = SqliteDatabase(':memory:')\n\nclass Reg(Model):\n    key = TextField()\n    value = IntegerField()\n\n    class Meta:\n        database = db\n\n\ndb.create_tables([Reg])\n\n# Create a user-defined aggregate function suitable for computing the standard\n# deviation of a series.\n@db.aggregate('stddev')\nclass StdDev(object):\n    def __init__(self):\n        self.n = 0\n        self.values = []\n\n    def step(self, value):\n        self.n += 1\n        self.values.append(value)\n\n    def finalize(self):\n        if self.n < 2:\n            return 0\n        mean = sum(self.values) / self.n\n        sqsum = sum((i - mean) ** 2 for i in self.values)\n        return math.sqrt(sqsum / (self.n - 1))\n\n\nvalues = [2, 3, 5, 2, 3, 12, 5, 3, 4, 1, 2, 1, -9, 3, 3, 5]\n\nReg.create_table()\nReg.insert_many([{'key': 'k%02d' % i, 'value': v}\n                 for i, v in enumerate(values)]).execute()\n\n# We'll calculate the mean and the standard deviation of the series in a common\n# table expression, which will then be used by our query to find rows whose\n# zscore exceeds a certain threshold.\ncte = (Reg\n       .select(fn.avg(Reg.value), fn.stddev(Reg.value))\n       .cte('stats', columns=('series_mean', 'series_stddev')))\n\n# The zscore is defined as the (value - mean) / stddev.\nzscore = (Reg.value - cte.c.series_mean) / cte.c.series_stddev\n\n# Find rows which fall outside of 2 standard deviations.\nthreshold = 2\nquery = (Reg\n         .select(Reg.key, Reg.value, zscore.alias('zscore'))\n         .from_(Reg, cte)\n         .where((zscore >= threshold) | (zscore <= -threshold))\n         .with_cte(cte))\n\nfor row in query:\n    print(row.key, row.value, round(row.zscore, 2))\n\ndb.close()\n"
  },
  {
    "path": "examples/blog/app.py",
    "content": "import datetime\nimport functools\nimport os\nimport re\nimport urllib\n\nfrom flask import (Flask, flash, redirect, render_template, request,\n                   Response, session, url_for)\nfrom markdown import markdown\nfrom markdown.extensions.codehilite import CodeHiliteExtension\nfrom markdown.extensions.extra import ExtraExtension\nfrom markupsafe import Markup\nfrom micawber import bootstrap_basic, parse_html\nfrom micawber.cache import Cache as OEmbedCache\nfrom peewee import *\nfrom playhouse.flask_utils import FlaskDB, get_object_or_404, object_list\nfrom playhouse.sqlite_ext import *\n\n\n# Blog configuration values.\n\n# You may consider using a one-way hash to generate the password, and then\n# use the hash again in the login view to perform the comparison. This is just\n# for simplicity.\nADMIN_PASSWORD = 'secret'\nAPP_DIR = os.path.dirname(os.path.realpath(__file__))\n\n# The playhouse.flask_utils.FlaskDB object accepts database URL configuration.\nDATABASE = 'sqlite:///%s?rank_functions=1' % os.path.join(APP_DIR, 'blog.db')\nDEBUG = False\n\n# The secret key is used internally by Flask to encrypt session data stored\n# in cookies. Make this unique for your app.\nSECRET_KEY = 'shhh, secret!'\n\n# This is used by micawber, which will attempt to generate rich media\n# embedded objects with maxwidth=800.\nSITE_WIDTH = 800\n\n\n# Create a Flask WSGI app and configure it using values from the module.\napp = Flask(__name__)\napp.config.from_object(__name__)\n\n# FlaskDB is a wrapper for a peewee database that sets up pre/post-request\n# hooks for managing database connections.\nflask_db = FlaskDB(app)\n\n# The `database` is the actual peewee database, as opposed to flask_db which is\n# the wrapper.\ndatabase = flask_db.database\n\n# Configure micawber with the default OEmbed providers (YouTube, Flickr, etc).\n# We'll use a simple in-memory cache so that multiple requests for the same\n# video don't require multiple network requests.\noembed_providers = bootstrap_basic(OEmbedCache())\n\n\nclass Entry(flask_db.Model):\n    title = CharField()\n    slug = CharField(unique=True)\n    content = TextField()\n    published = BooleanField(index=True)\n    timestamp = DateTimeField(default=datetime.datetime.now, index=True)\n\n    @property\n    def html_content(self):\n        \"\"\"\n        Generate HTML representation of the markdown-formatted blog entry,\n        and also convert any media URLs into rich media objects such as video\n        players or images.\n        \"\"\"\n        hilite = CodeHiliteExtension(linenums=False, css_class='highlight')\n        extras = ExtraExtension()\n        markdown_content = markdown(self.content, extensions=[hilite, extras])\n        oembed_content = parse_html(\n            markdown_content,\n            oembed_providers,\n            urlize_all=True,\n            maxwidth=app.config['SITE_WIDTH'])\n        return Markup(oembed_content)\n\n    def save(self, *args, **kwargs):\n        # Generate a URL-friendly representation of the entry's title.\n        if not self.slug:\n            self.slug = re.sub(r'[^\\w]+', '-', self.title.lower()).strip('-')\n        ret = super(Entry, self).save(*args, **kwargs)\n\n        # Store search content.\n        self.update_search_index()\n        return ret\n\n    def update_search_index(self):\n        # Create a row in the FTSEntry table with the post content. This will\n        # allow us to use SQLite's awesome full-text search extension to\n        # search our entries.\n        exists = (FTSEntry\n                  .select(FTSEntry.docid)\n                  .where(FTSEntry.docid == self.id)\n                  .exists())\n        content = '\\n'.join((self.title, self.content))\n        if exists:\n            (FTSEntry\n             .update({FTSEntry.content: content})\n             .where(FTSEntry.docid == self.id)\n             .execute())\n        else:\n            FTSEntry.insert({\n                FTSEntry.docid: self.id,\n                FTSEntry.content: content}).execute()\n\n    @classmethod\n    def public(cls):\n        return Entry.select().where(Entry.published == True)\n\n    @classmethod\n    def drafts(cls):\n        return Entry.select().where(Entry.published == False)\n\n    @classmethod\n    def search(cls, query):\n        words = [word.strip() for word in query.split() if word.strip()]\n        if not words:\n            # Return an empty query.\n            return Entry.noop()\n        else:\n            search = ' '.join(words)\n\n        # Query the full-text search index for entries matching the given\n        # search query, then join the actual Entry data on the matching\n        # search result.\n        return (Entry\n                .select(Entry, FTSEntry.rank().alias('score'))\n                .join(FTSEntry, on=(Entry.id == FTSEntry.docid))\n                .where(\n                    FTSEntry.match(search) &\n                    (Entry.published == True))\n                .order_by(SQL('score')))\n\nclass FTSEntry(FTSModel):\n    content = TextField()\n\n    class Meta:\n        database = database\n\ndef login_required(fn):\n    @functools.wraps(fn)\n    def inner(*args, **kwargs):\n        if session.get('logged_in'):\n            return fn(*args, **kwargs)\n        return redirect(url_for('login', next=request.path))\n    return inner\n\n@app.route('/login/', methods=['GET', 'POST'])\ndef login():\n    next_url = request.args.get('next') or request.form.get('next')\n    if request.method == 'POST' and request.form.get('password'):\n        password = request.form.get('password')\n        # TODO: If using a one-way hash, you would also hash the user-submitted\n        # password and do the comparison on the hashed versions.\n        if password == app.config['ADMIN_PASSWORD']:\n            session['logged_in'] = True\n            session.permanent = True  # Use cookie to store session.\n            flash('You are now logged in.', 'success')\n            return redirect(next_url or url_for('index'))\n        else:\n            flash('Incorrect password.', 'danger')\n    return render_template('login.html', next_url=next_url)\n\n@app.route('/logout/', methods=['GET', 'POST'])\ndef logout():\n    if request.method == 'POST':\n        session.clear()\n        return redirect(url_for('login'))\n    return render_template('logout.html')\n\n@app.route('/')\ndef index():\n    search_query = request.args.get('q')\n    if search_query:\n        query = Entry.search(search_query)\n    else:\n        query = Entry.public().order_by(Entry.timestamp.desc())\n\n    # The `object_list` helper will take a base query and then handle\n    # paginating the results if there are more than 20. For more info see\n    # the docs:\n    # http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#object_list\n    return object_list(\n        'index.html',\n        query,\n        search=search_query,\n        check_bounds=False)\n\ndef _create_or_edit(entry, template):\n    if request.method == 'POST':\n        entry.title = request.form.get('title') or ''\n        entry.content = request.form.get('content') or ''\n        entry.published = request.form.get('published') or False\n        if not (entry.title and entry.content):\n            flash('Title and Content are required.', 'danger')\n        else:\n            # Wrap the call to save in a transaction so we can roll it back\n            # cleanly in the event of an integrity error.\n            try:\n                with database.atomic():\n                    entry.save()\n            except IntegrityError:\n                flash('Error: this title is already in use.', 'danger')\n            else:\n                flash('Entry saved successfully.', 'success')\n                if entry.published:\n                    return redirect(url_for('detail', slug=entry.slug))\n                else:\n                    return redirect(url_for('edit', slug=entry.slug))\n\n    return render_template(template, entry=entry)\n\n@app.route('/create/', methods=['GET', 'POST'])\n@login_required\ndef create():\n    return _create_or_edit(Entry(title='', content=''), 'create.html')\n\n@app.route('/drafts/')\n@login_required\ndef drafts():\n    query = Entry.drafts().order_by(Entry.timestamp.desc())\n    return object_list('index.html', query, check_bounds=False)\n\n@app.route('/<slug>/')\ndef detail(slug):\n    if session.get('logged_in'):\n        query = Entry.select()\n    else:\n        query = Entry.public()\n    entry = get_object_or_404(query, Entry.slug == slug)\n    return render_template('detail.html', entry=entry)\n\n@app.route('/<slug>/edit/', methods=['GET', 'POST'])\n@login_required\ndef edit(slug):\n    entry = get_object_or_404(Entry, Entry.slug == slug)\n    return _create_or_edit(entry, 'edit.html')\n\n@app.template_filter('clean_querystring')\ndef clean_querystring(request_args, *keys_to_remove, **new_values):\n    # We'll use this template filter in the pagination include. This filter\n    # will take the current URL and allow us to preserve the arguments in the\n    # querystring while replacing any that we need to overwrite. For instance\n    # if your URL is /?q=search+query&page=2 and we want to preserve the search\n    # term but make a link to page 3, this filter will allow us to do that.\n    querystring = dict((key, value) for key, value in request_args.items())\n    for key in keys_to_remove:\n        querystring.pop(key, None)\n    querystring.update(new_values)\n    return urllib.urlencode(querystring)\n\n@app.errorhandler(404)\ndef not_found(exc):\n    return Response('<h3>Not found</h3>'), 404\n\ndef main():\n    database.create_tables([Entry, FTSEntry], safe=True)\n    app.run(debug=True)\n\nif __name__ == '__main__':\n    print('To login, open:')\n    print('http://127.0.0.1:5000/login/')\n    print('password is: %s' % ADMIN_PASSWORD)\n    main()\n"
  },
  {
    "path": "examples/blog/requirements.txt",
    "content": "flask\nbeautifulsoup4\nmicawber\npygments\nmarkdown\npeewee\n"
  },
  {
    "path": "examples/blog/static/css/hilite.css",
    "content": ".highlight {\n\tbackground: #040400;\n\tcolor: #FFFFFF;\n}\n\n.highlight span.selection { color: #323232; }\n.highlight span.gp { color: #9595FF; }\n.highlight span.vi { color: #9595FF; }\n.highlight span.kn { color: #00C0D1; }\n.highlight span.cp { color: #AEE674; }\n.highlight span.caret { color: #FFFFFF; }\n.highlight span.no { color: #AEE674; }\n.highlight span.s2 { color: #BBFB8D; }\n.highlight span.nb { color: #A7FDB2; }\n.highlight span.nc { color: #C2ABFF; }\n.highlight span.nd { color: #AEE674; }\n.highlight span.s { color: #BBFB8D; }\n.highlight span.nf { color: #AEE674; }\n.highlight span.nx { color: #AEE674; }\n.highlight span.kp { color: #00C0D1; }\n.highlight span.nt { color: #C2ABFF; }\n.highlight span.s1 { color: #BBFB8D; }\n.highlight span.bg { color: #040400; }\n.highlight span.kt { color: #00C0D1; }\n.highlight span.support_function { color: #81B864; }\n.highlight span.ow { color: #EBE1B4; }\n.highlight span.mf { color: #A1FF24; }\n.highlight span.bp { color: #9595FF; }\n.highlight span.fg { color: #FFFFFF; }\n.highlight span.c1 { color: #3379FF; }\n.highlight span.kc { color: #9595FF; }\n.highlight span.c { color: #3379FF; }\n.highlight span.sx { color: #BBFB8D; }\n.highlight span.kd { color: #00C0D1; }\n.highlight span.ss { color: #A1FF24; }\n.highlight span.sr { color: #BBFB8D; }\n.highlight span.mo { color: #A1FF24; }\n.highlight span.mi { color: #A1FF24; }\n.highlight span.mh { color: #A1FF24; }\n.highlight span.o { color: #EBE1B4; }\n.highlight span.si { color: #DA96A3; }\n.highlight span.sh { color: #BBFB8D; }\n.highlight span.na { color: #AEE674; }\n.highlight span.sc { color: #BBFB8D; }\n.highlight span.k { color: #00C0D1; }\n.highlight span.se { color: #DA96A3; }\n.highlight span.sd { color: #54F79C; }\n"
  },
  {
    "path": "examples/blog/static/robots.txt",
    "content": "User-agent: *\n"
  },
  {
    "path": "examples/blog/templates/base.html",
    "content": "<!doctype html>\n<html>\n  <head>\n    <title>Blog</title>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <meta name=\"robots\" content=\"noindex\">\n    <link rel=stylesheet type=text/css href=\"{{ url_for('static', filename='css/blog.min.css') }}\" />\n    <link rel=stylesheet type=text/css href=\"{{ url_for('static', filename='css/hilite.css') }}\" />\n    {% block extra_head %}{% endblock %}\n    <script src=\"{{ url_for('static', filename='js/jquery-1.11.0.min.js') }}\" type=\"text/javascript\"></script>\n    <script src=\"{{ url_for('static', filename='js/bootstrap.min.js') }}\"></script>\n    {% block extra_scripts %}{% endblock %}\n  </head>\n\n  <body>\n    <div class=\"navbar navbar-default navbar-static-top\" role=\"navigation\">\n      <div class=\"container\">\n        <div class=\"navbar-header\">\n          <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\".navbar-collapse\">\n            <span class=\"sr-only\">Toggle navigation</span>\n            <span class=\"icon-bar\"></span>\n            <span class=\"icon-bar\"></span>\n            <span class=\"icon-bar\"></span>\n          </button>\n          <a class=\"navbar-brand\" href=\"{{ url_for('index') }}\">Blog</a>\n        </div>\n        <div class=\"navbar-collapse collapse\">\n          <ul class=\"nav navbar-nav\">\n            {% if session.logged_in %}\n              <li><a href=\"{{ url_for('drafts') }}\">Drafts</a></li>\n              <li><a href=\"{{ url_for('create') }}\">Create Entry</a></li>\n              <li><a href=\"{{ url_for('logout') }}\">Log out</a></li>\n            {% endif %}\n            {% block extra_header %}{% endblock %}\n          </ul>\n          {% block search_bar %}\n            <form action=\"{{ url_for('index') }}\" class=\"navbar-form navbar-right\" id=\"search-form\" method=\"get\" role=\"search\">\n              <div class=\"form-group\">\n                <input class=\"form-control\" name=\"q\" placeholder=\"Search\" type=\"text\" value=\"{% if search %}{{ search }}{% endif %}\">\n              </div>\n            </form>\n          {% endblock %}\n        </div>\n      </div>\n    </div>\n\n    <div class=\"container\">\n      {% for category, message in get_flashed_messages(with_categories=true) %}\n        <div class=\"alert alert-{{ category }} alert-dismissable\">\n          <button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times;</button>\n          <p>{{ message }}</p>\n        </div>\n      {% endfor %}\n\n      {% block page_header %}\n        <div class=\"page-header\">\n          <h1>{% block content_title %}{% endblock %}</h1>\n        </div>\n      {% endblock %}\n\n      {% block content %}{% endblock %}\n\n      <footer>\n        <hr />\n        <p>Blog, &copy; 2015</p>\n      </footer>\n    </div>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/blog/templates/create.html",
    "content": "{% extends \"base.html\" %}\n\n{% block title %}Create entry{% endblock %}\n\n{% block content_title %}Create entry{% endblock %}\n\n{% block content %}\n  <form action=\"{% block form_action %}{{ url_for('create') }}{% endblock %}\" class=\"form-horizontal\" method=\"post\">\n    <div class=\"form-group\">\n      <label for=\"title\" class=\"col-sm-2 control-label\">Title</label>\n      <div class=\"col-sm-10\">\n        <input class=\"form-control\" id=\"title\" name=\"title\" type=\"text\" value=\"{{ entry.title }}\">\n      </div>\n    </div>\n    <div class=\"form-group\">\n      <label for=\"Content\" class=\"col-sm-2 control-label\">Content</label>\n      <div class=\"col-sm-10\">\n        <textarea class=\"form-control\" id=\"content\" name=\"content\" style=\"height: 300px;\">{{ entry.content }}</textarea>\n      </div>\n    </div>\n    <div class=\"form-group\">\n      <div class=\"col-sm-offset-2 col-sm-10\">\n        <div class=\"checkbox\">\n          <label>\n            <input name=\"published\" type=\"checkbox\" value=\"y\"{% if entry.published %} checked=\"checked\"{% endif %}> Published?\n          </label>\n        </div>\n      </div>\n    </div>\n    <div class=\"form-group\">\n      <div class=\"col-sm-offset-2 col-sm-10\">\n        <button class=\"btn btn-primary\" type=\"submit\">{% block save_button %}Create{% endblock %}</button>\n        <a class=\"btn btn-default\" href=\"{{ url_for('index') }}\">Cancel</a>\n      </div>\n    </div>\n  </form>\n{% endblock %}\n"
  },
  {
    "path": "examples/blog/templates/detail.html",
    "content": "{% extends \"base.html\" %}\n\n{% block title %}{{ entry.title }}{% endblock %}\n\n{% block content_title %}{{ entry.title }}{% endblock %}\n\n{% block extra_header %}\n  {% if session.logged_in %}\n    <li><a href=\"{{ url_for('edit', slug=entry.slug) }}\">Edit entry</a></li>\n  {% endif %}\n{% endblock %}\n\n{% block content %}\n  <p>Created {{ entry.timestamp.strftime('%m/%d/%Y at %I:%M%p') }}</p>\n  {{ entry.html_content }}\n{% endblock %}\n"
  },
  {
    "path": "examples/blog/templates/edit.html",
    "content": "{% extends \"create.html\" %}\n\n{% block title %}Edit entry{% endblock %}\n\n{% block content_title %}Edit entry{% endblock %}\n\n{% block form_action %}{{ url_for('edit', slug=entry.slug) }}{% endblock %}\n\n{% block save_button %}Save changes{% endblock %}\n"
  },
  {
    "path": "examples/blog/templates/includes/pagination.html",
    "content": "{% if pagination.get_page_count() > 1 %}\n<ul class=\"pager\">\n  {% if pagination.get_page() > 1 %}\n    <li class=\"previous\"><a href=\"./?{{ request.args|clean_querystring('page', page=pagination.get_page() - 1) }}\">&laquo; Previous {{ pagination.get_page() - 1 }} / {{ pagination.get_page_count() }}</a></li>\n  {% else %}\n    <li class=\"previous disabled\"><a href=\"#\">&laquo; Previous</a></li>\n  {% endif %}\n  {% if pagination.get_page_count() > pagination.get_page() %}\n    <li class=\"next\"><a href=\"./?{{ request.args|clean_querystring('page', page=pagination.get_page() + 1) }}\">Next {{ pagination.get_page() + 1 }} / {{ pagination.get_page_count() }} &raquo;</a></li>\n  {% else %}\n    <li class=\"next disabled\"><a href=\"#\">Next &raquo;</a></li>\n  {% endif %}\n</ul>\n{% endif %}\n"
  },
  {
    "path": "examples/blog/templates/index.html",
    "content": "{% extends \"base.html\" %}\n\n{% block title %}Blog entries{% endblock %}\n\n{% block content_title %}{% if search %}Search \"{{ search }}\"{% else %}Blog entries{% endif %}{% endblock %}\n\n{% block content %}\n  {% for entry in object_list %}\n    <h3>\n      <a href=\"{% if entry.published %}{{ url_for('detail', slug=entry.slug) }}{% else %}{{ url_for('edit', slug=entry.slug) }}{% endif %}\">\n        {{ entry.title }}\n      </a>\n    </h3>\n    <p>Created {{ entry.timestamp.strftime('%m/%d/%Y at %G:%I%p') }}</p>\n  {% else %}\n    <p>No entries have been created yet.</p>\n  {% endfor %}\n  {% include \"includes/pagination.html\" %}\n{% endblock %}\n"
  },
  {
    "path": "examples/blog/templates/login.html",
    "content": "{% extends \"base.html\" %}\n\n{% block title %}Log in{% endblock %}\n\n{% block content_title %}Log in{% endblock %}\n\n{% block content %}\n  <form action=\"{{ url_for('login', next=next_url) }}\" class=\"form-horizontal\" method=\"post\">\n    <div class=\"form-group\">\n      <label for=\"password\" class=\"col-sm-2 control-label\">Password</label>\n      <div class=\"col-sm-10\">\n        <input class=\"form-control\" id=\"password\" name=\"password\" type=\"password\">\n      </div>\n    </div>\n    <div class=\"form-group\">\n      <div class=\"col-sm-offset-2 col-sm-10\">\n        <button class=\"btn btn-default\" type=\"submit\">Log in</button>\n      </div>\n    </div>\n  </form>\n{% endblock %}\n"
  },
  {
    "path": "examples/blog/templates/logout.html",
    "content": "{% extends \"base.html\" %}\n\n{% block title %}Log out{% endblock %}\n\n{% block content_title %}Log out{% endblock %}\n\n{% block content %}\n  <p>Click the button below to log out of the site.</p>\n  <form action=\"{{ url_for('logout') }}\" class=\"form-horizontal\" method=\"post\">\n    <button class=\"btn btn-primary\" type=\"submit\">Log out</button>\n  </form>\n{% endblock %}\n"
  },
  {
    "path": "examples/diary.py",
    "content": "#!/usr/bin/env python\n\nfrom collections import OrderedDict\nimport datetime\nfrom getpass import getpass\nimport sys\n\nfrom peewee import *\nfrom playhouse.sqlcipher_ext import SqlCipherDatabase\n\n# Defer initialization of the database until the script is executed from the\n# command-line.\ndb = SqlCipherDatabase(None)\n\n\nclass Entry(Model):\n    content = TextField()\n    timestamp = DateTimeField(default=datetime.datetime.now)\n\n    class Meta:\n        database = db\n\n\ndef initialize(passphrase):\n    db.init('diary.db', passphrase=passphrase)\n    db.create_tables([Entry])\n\n\ndef menu_loop():\n    choice = None\n    while choice != 'q':\n        for key, value in menu.items():\n            print('%s) %s' % (key, value.__doc__))\n        choice = input('Action: ').lower().strip()\n        if choice in menu:\n            menu[choice]()\n\n\ndef add_entry():\n    \"\"\"Add entry\"\"\"\n    print('Enter your entry. Press ctrl+d when finished.')\n    data = sys.stdin.read().strip()\n    if data and input('Save entry? [Yn] ') != 'n':\n        Entry.create(content=data)\n        print('Saved successfully.')\n\n\ndef view_entries(search_query=None):\n    \"\"\"View previous entries\"\"\"\n    query = Entry.select().order_by(Entry.timestamp.desc())\n    if search_query:\n        query = query.where(Entry.content.contains(search_query))\n\n    for entry in query:\n        timestamp = entry.timestamp.strftime('%A %B %d, %Y %I:%M%p')\n        print(timestamp)\n        print('=' * len(timestamp))\n        print(entry.content)\n        print('n) next entry')\n        print('d) delete entry')\n        print('q) return to main menu')\n        action = input('Choice? (Ndq) ').lower().strip()\n        if action == 'q':\n            break\n        elif action == 'd':\n            entry.delete_instance()\n            break\n\n\ndef search_entries():\n    \"\"\"Search entries\"\"\"\n    view_entries(input('Search query: '))\n\n\nmenu = OrderedDict([\n    ('a', add_entry),\n    ('v', view_entries),\n    ('s', search_entries),\n])\n\nif __name__ == '__main__':\n    # Collect the passphrase using a secure method.\n    passphrase = getpass('Enter password: ')\n\n    if not passphrase:\n        sys.stderr.write('Passphrase required to access diary.\\n')\n        sys.stderr.flush()\n        sys.exit(1)\n    elif len(passphrase) < 8:\n        sys.stderr.write('Passphrase must be at least 8 characters.\\n')\n        sys.stderr.flush()\n        sys.exit(1)\n\n    # Initialize the database.\n    initialize(passphrase)\n    menu_loop()\n"
  },
  {
    "path": "examples/graph.py",
    "content": "from peewee import *\n\n\ndb = SqliteDatabase(':memory:')\n\n\nclass Base(Model):\n    class Meta:\n        database = db\n\n\nclass Node(Base):\n    name = TextField(primary_key=True)\n\n    def outgoing(self):\n        return (Node\n                .select(Node, Edge.weight)\n                .join(Edge, on=Edge.dest)\n                .where(Edge.src == self)\n                .objects())\n\n    def incoming(self):\n        return (Node\n                .select(Node, Edge.weight)\n                .join(Edge, on=Edge.src)\n                .where(Edge.dest == self)\n                .objects())\n\n\nclass Edge(Base):\n    src = ForeignKeyField(Node, backref='outgoing_edges')\n    dest = ForeignKeyField(Node, backref='incoming_edges')\n    weight = FloatField()\n\n\ndb.create_tables([Node, Edge])\n\n\nnodes = [Node.create(name=c) for c in 'abcde']\ng = (\n    ('a', 'b', -1),\n    ('a', 'c', 4),\n    ('b', 'c', 3),\n    ('b', 'd', 2),\n    ('b', 'e', 2),\n    ('d', 'b', 1),\n    ('d', 'c', 5),\n    ('e', 'd', -3))\nfor src, dest, wt in g:\n    src_n, dest_n = nodes[ord(src) - ord('a')], nodes[ord(dest) - ord('a')]\n    Edge.create(src=src_n, dest=dest_n, weight=wt)\n\n\ndef bellman_ford(s):\n    dist = {}\n    pred = {}\n    all_nodes = Node.select()\n    for node in all_nodes:\n        dist[node] = float('inf')\n        pred[node] = None\n    dist[s] = 0\n\n    for _ in range(len(all_nodes) - 1):\n        for u in all_nodes:\n            for v in u.outgoing():\n                potential = dist[u] + v.weight\n                if dist[v] > potential:\n                    dist[v] = potential\n                    pred[v] = u\n\n    # Verify no negative-weight cycles.\n    for u in all_nodes:\n        for v in u.outgoing():\n            assert dist[v] <= dist[u] + v.weight\n\n    return dist, pred\n\ndef print_path(s, e):\n    dist, pred = bellman_ford(s)\n    distance = dist[e]\n    route = [e]\n    while e != s:\n        route.append(pred[e])\n        e = pred[e]\n\n    print(' -> '.join(v.name for v in route[::-1]) + ' (%s)' % distance)\n\nprint_path(Node['a'], Node['c'])  # a -> b -> c\nprint_path(Node['a'], Node['d'])  # a -> b -> e -> d\nprint_path(Node['b'], Node['d'])  # b -> e -> d\n"
  },
  {
    "path": "examples/hexastore.py",
    "content": "try:\n    from functools import reduce\nexcept ImportError:\n    pass\nimport operator\n\nfrom peewee import *\n\n\nclass Hexastore(object):\n    def __init__(self, database=':memory:', **options):\n        if isinstance(database, str):\n            self.db = SqliteDatabase(database, **options)\n        elif isinstance(database, Database):\n            self.db = database\n        else:\n            raise ValueError('Expected database filename or a Database '\n                             'instance. Got: %s' % repr(database))\n\n        self.v = _VariableFactory()\n        self.G = self.get_model()\n\n    def get_model(self):\n        class Graph(Model):\n            subj = TextField()\n            pred = TextField()\n            obj = TextField()\n            class Meta:\n                database = self.db\n                indexes = (\n                    (('pred', 'obj'), False),\n                    (('obj', 'subj'), False),\n                )\n                primary_key = CompositeKey('subj', 'pred', 'obj')\n\n        self.db.create_tables([Graph])\n        return Graph\n\n    def store(self, s, p, o):\n        self.G.create(subj=s, pred=p, obj=o)\n\n    def store_many(self, items):\n        fields = [self.G.subj, self.G.pred, self.G.obj]\n        self.G.insert_many(items, fields=fields).execute()\n\n    def delete(self, s, p, o):\n        return (self.G.delete()\n                .where(self.G.subj == s, self.G.pred == p, self.G.obj == o)\n                .execute())\n\n    def query(self, s=None, p=None, o=None):\n        fields = (self.G.subj, self.G.pred, self.G.obj)\n        expressions = [(f == v) for f, v in zip(fields, (s, p, o))\n                       if v is not None]\n        return self.G.select().where(*expressions)\n\n    def search(self, *conditions):\n        accum = []\n        binds = {}\n        variables = set()\n        fields = {'s': 'subj', 'p': 'pred', 'o': 'obj'}\n\n        for i, condition in enumerate(conditions):\n            if isinstance(condition, dict):\n                condition = (condition['s'], condition['p'], condition['o'])\n\n            GA = self.G.alias('g%s' % i)\n            for part, val in zip('spo', condition):\n                if isinstance(val, Variable):\n                    binds.setdefault(val, [])\n                    binds[val].append(getattr(GA, fields[part]))\n                    variables.add(val)\n                else:\n                    accum.append(getattr(GA, fields[part]) == val)\n\n        selection = []\n        sources = set()\n\n        for var, fields in binds.items():\n            selection.append(fields[0].alias(var.name))\n            pairwise = [(fields[i - 1] == fields[i])\n                        for i in range(1, len(fields))]\n            if pairwise:\n                accum.append(reduce(operator.and_, pairwise))\n            sources.update([field.source for field in fields])\n\n        return (self.G\n                .select(*selection)\n                .from_(*list(sources))\n                .where(*accum)\n                .dicts())\n\n\nclass _VariableFactory(object):\n    def __getattr__(self, name):\n        return Variable(name)\n    __call__ = __getattr__\n\nclass Variable(object):\n    __slots__ = ('name',)\n\n    def __init__(self, name):\n        self.name = name\n\n    def __hash__(self):\n        return hash(self.name)\n\n    def __repr__(self):\n        return '<Variable: %s>' % self.name\n\n\nif __name__ == '__main__':\n    h = Hexastore()\n\n    data = (\n        ('charlie', 'likes', 'beanie'),\n        ('charlie', 'likes', 'huey'),\n        ('charlie', 'likes', 'mickey'),\n        ('charlie', 'likes', 'scout'),\n        ('charlie', 'likes', 'zaizee'),\n\n        ('huey', 'likes', 'charlie'),\n        ('huey', 'likes', 'scout'),\n        ('huey', 'likes', 'zaizee'),\n\n        ('mickey', 'likes', 'beanie'),\n        ('mickey', 'likes', 'charlie'),\n        ('mickey', 'likes', 'scout'),\n\n        ('zaizee', 'likes', 'beanie'),\n        ('zaizee', 'likes', 'charlie'),\n        ('zaizee', 'likes', 'scout'),\n\n        ('charlie', 'lives', 'topeka'),\n        ('beanie', 'lives', 'heaven'),\n        ('huey', 'lives', 'topeka'),\n        ('mickey', 'lives', 'topeka'),\n        ('scout', 'lives', 'heaven'),\n        ('zaizee', 'lives', 'lawrence'),\n    )\n    h.store_many(data)\n    print('added %s items to store' % len(data))\n\n    print('\\nwho lives in topeka?')\n    for obj in h.query(p='lives', o='topeka'):\n        print(obj.subj)\n\n    print('\\nmy friends in heaven?')\n    X = h.v.x\n    results = h.search(('charlie', 'likes', X),\n                       (X, 'lives', 'heaven'))\n    for result in results:\n        print(result['x'])\n\n    print('\\nmutual friends?')\n    X = h.v.x\n    Y = h.v.y\n    results = h.search((X, 'likes', Y), (Y, 'likes', X))\n    for result in results:\n        print(result['x'], ' <-> ', result['y'])\n\n    print('\\nliked by both charlie, huey and mickey?')\n    X = h.v.x\n    results = h.search(('charlie', 'likes', X),\n                       ('huey', 'likes', X),\n                       ('mickey', 'likes', X))\n    for result in results:\n        print(result['x'])\n"
  },
  {
    "path": "examples/query_library.py",
    "content": "# Collection of Query Examples.\n# https://docs.peewee-orm.com/en/latest/peewee/query_library.html\n\nfrom functools import partial\nfrom peewee import *\n\n\ndb = PostgresqlDatabase('peewee_clubdata')\n\nclass BaseModel(Model):\n    class Meta:\n        database = db\n\nclass Member(BaseModel):\n    memid = AutoField()  # Auto-incrementing primary key.\n    surname = CharField()\n    firstname = CharField()\n    address = CharField(max_length=300)\n    zipcode = IntegerField()\n    telephone = CharField()\n    recommendedby = ForeignKeyField('self', backref='recommended',\n                                    column_name='recommendedby', null=True)\n    joindate = DateTimeField()\n\n    class Meta:\n        table_name = 'members'\n\n\n# Conveniently declare decimal fields suitable for storing currency.\nMoneyField = partial(DecimalField, decimal_places=2)\n\n\nclass Facility(BaseModel):\n    facid = AutoField()\n    name = CharField()\n    membercost = MoneyField()\n    guestcost = MoneyField()\n    initialoutlay = MoneyField()\n    monthlymaintenance = MoneyField()\n\n    class Meta:\n        table_name = 'facilities'\n\n\nclass Booking(BaseModel):\n    bookid = AutoField()\n    facility = ForeignKeyField(Facility, column_name='facid')\n    member = ForeignKeyField(Member, column_name='memid')\n    starttime = DateTimeField()\n    slots = IntegerField()\n\n    class Meta:\n        table_name = 'bookings'\n"
  },
  {
    "path": "examples/reddit_ranking.py",
    "content": "import datetime\nimport math\n\nfrom peewee import *\nfrom peewee import query_to_string\n\n\ndb = SqliteDatabase(':memory:')\n\n@db.func('log')\ndef log(n, b):\n    return math.log(n, b)\n\nclass Base(Model):\n    class Meta:\n        database = db\n\nclass Post(Base):\n    content = TextField()\n    timestamp = TimestampField()\n    ups = IntegerField(default=0)\n    downs = IntegerField(default=0)\n\n\ndb.create_tables([Post])\n\n# Populate with a number of posts.\ndata = (\n    # Hours ago, ups, downs.\n    (1, 5, 0),\n    (1, 7, 1),\n    (2, 10, 2),\n    (2, 2, 0),\n    (2, 1, 2),\n    (3, 11, 2),\n    (4, 20, 2),\n    (4, 60, 12),\n    (5, 3, 0),\n    (5, 1, 0),\n    (6, 30, 3),\n    (6, 30, 20),\n    (7, 45, 10),\n    (7, 45, 20),\n    (8, 11, 2),\n    (8, 3, 1),\n)\n\nnow = datetime.datetime.now()\nPost.insert_many([\n    ('post %2dh %2d up, %2d down' % (hours, ups, downs),\n     now - datetime.timedelta(seconds=hours * 3600),\n     ups,\n     downs) for hours, ups, downs in data]).execute()\n\n\nscore = (Post.ups - Post.downs)\norder = fn.log(fn.max(fn.abs(score), 1), 10)\nsign = Case(None, (\n    ((score > 0), 1),\n    ((score < 0), -1)), 0)\nseconds = (Post.timestamp) - 1134028003\n\nhot = (sign * order) + (seconds / 45000)\nquery = Post.select(Post.content, hot.alias('score')).order_by(SQL('score').desc())\n#print(query_to_string(query))\nprint('Posts, ordered best-to-worse:')\n\nfor post in query:\n    print(post.content, round(post.score, 3))\n"
  },
  {
    "path": "examples/sqlite_fts_compression.py",
    "content": "#\n# Small example demonstrating the use of zlib compression with the Sqlite\n# full-text search extension.\n#\nimport zlib\n\nfrom peewee import *\nfrom playhouse.sqlite_ext import *\n\n\ndb = SqliteDatabase(':memory:', rank_functions=True)\n\nclass SearchIndex(FTSModel):\n    content = SearchField()\n\n    class Meta:\n        database = db\n\n\n@db.func('zlib_compress')\ndef _zlib_compress(data):\n    if data is not None:\n        if isinstance(data, str):\n            data = data.encode('utf8')\n        return zlib.compress(data, 9)\n\n@db.func('zlib_decompress')\ndef _zlib_decompress(data):\n    if data is not None:\n        return zlib.decompress(data)\n\n\nSearchIndex.create_table(\n    tokenize='porter',\n    compress='zlib_compress',\n    uncompress='zlib_decompress')\n\nphrases = [\n    'A faith is a necessity to a man. Woe to him who believes in nothing.',\n    ('All who call on God in true faith, earnestly from the heart, will '\n     'certainly be heard, and will receive what they have asked and desired.'),\n    ('Be faithful in small things because it is in them that your strength '\n     'lies.'),\n    ('Faith consists in believing when it is beyond the power of reason to '\n     'believe.'),\n    ('Faith has to do with things that are not seen and hope with things that '\n     'are not at hand.')]\n\nfor phrase in phrases:\n    SearchIndex.create(content=phrase)\n\n# Use the simple ranking algorithm.\nquery = SearchIndex.search('faith things', with_score=True)\nfor row in query:\n    print(round(row.score, 2), row.content.decode('utf8'))\n\nprint('---')\n\n# Use the Okapi-BM25 ranking algorithm.\nquery = SearchIndex.search_bm25('believe', with_score=True)\nfor row in query:\n    print(round(row.score, 2), row.content.decode('utf8'))\n\ndb.close()\n"
  },
  {
    "path": "examples/twitter/app.py",
    "content": "import datetime\n\nfrom flask import Flask\nfrom flask import g\nfrom flask import redirect\nfrom flask import request\nfrom flask import session\nfrom flask import url_for, abort, render_template, flash\nfrom functools import wraps\nfrom hashlib import md5\nfrom peewee import *\n\n# config - aside from our database, the rest is for use by Flask\nDATABASE = 'tweepee.db'\nDEBUG = True\nSECRET_KEY = 'hin6bab8ge25*r=x&amp;+5$0kn=-#log$pt^#@vrqjld!^2ci@g*b'\n\n# create a flask application - this ``app`` object will be used to handle\n# inbound requests, routing them to the proper 'view' functions, etc\napp = Flask(__name__)\napp.config.from_object(__name__)\n\n# create a peewee database instance -- our models will use this database to\n# persist information\ndatabase = SqliteDatabase(DATABASE)\n\n# model definitions -- the standard \"pattern\" is to define a base model class\n# that specifies which database to use.  then, any subclasses will automatically\n# use the correct storage. for more information, see:\n# https://charlesleifer.com/docs/peewee/peewee/models.html#model-api-smells-like-django\nclass BaseModel(Model):\n    class Meta:\n        database = database\n\n# the user model specifies its fields (or columns) declaratively, like django\nclass User(BaseModel):\n    username = CharField(unique=True)\n    password = CharField()\n    email = CharField()\n    join_date = DateTimeField()\n\n    # it often makes sense to put convenience methods on model instances, for\n    # example, \"give me all the users this user is following\":\n    def following(self):\n        # query other users through the \"relationship\" table\n        return (User\n                .select()\n                .join(Relationship, on=Relationship.to_user)\n                .where(Relationship.from_user == self)\n                .order_by(User.username))\n\n    def followers(self):\n        return (User\n                .select()\n                .join(Relationship, on=Relationship.from_user)\n                .where(Relationship.to_user == self)\n                .order_by(User.username))\n\n    def is_following(self, user):\n        return (Relationship\n                .select()\n                .where(\n                    (Relationship.from_user == self) &\n                    (Relationship.to_user == user))\n                .exists())\n\n    def gravatar_url(self, size=80):\n        return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \\\n            (md5(self.email.strip().lower().encode('utf-8')).hexdigest(), size)\n\n\n# this model contains two foreign keys to user -- it essentially allows us to\n# model a \"many-to-many\" relationship between users.  by querying and joining\n# on different columns we can expose who a user is \"related to\" and who is\n# \"related to\" a given user\nclass Relationship(BaseModel):\n    from_user = ForeignKeyField(User, backref='relationships')\n    to_user = ForeignKeyField(User, backref='related_to')\n\n    class Meta:\n        indexes = (\n            # Specify a unique multi-column index on from/to-user.\n            (('from_user', 'to_user'), True),\n        )\n\n\n# a dead simple one-to-many relationship: one user has 0..n messages, exposed by\n# the foreign key.  because we didn't specify, a users messages will be accessible\n# as a special attribute, User.message_set\nclass Message(BaseModel):\n    user = ForeignKeyField(User, backref='messages')\n    content = TextField()\n    pub_date = DateTimeField()\n\n\n# simple utility function to create tables\ndef create_tables():\n    with database:\n        database.create_tables([User, Relationship, Message])\n\n# flask provides a \"session\" object, which allows us to store information across\n# requests (stored by default in a secure cookie).  this function allows us to\n# mark a user as being logged-in by setting some values in the session data:\ndef auth_user(user):\n    session['logged_in'] = True\n    session['user_id'] = user.id\n    session['username'] = user.username\n    flash('You are logged in as %s' % (user.username))\n\n# get the user from the session\ndef get_current_user():\n    if session.get('logged_in'):\n        return User.get(User.id == session['user_id'])\n\n# view decorator which indicates that the requesting user must be authenticated\n# before they can access the view.  it checks the session to see if they're\n# logged in, and if not redirects them to the login view.\ndef login_required(f):\n    @wraps(f)\n    def inner(*args, **kwargs):\n        if not session.get('logged_in'):\n            return redirect(url_for('login'))\n        return f(*args, **kwargs)\n    return inner\n\n# given a template and a SelectQuery instance, render a paginated list of\n# objects from the query inside the template\ndef object_list(template_name, qr, var_name='object_list', **kwargs):\n    kwargs.update(\n        page=int(request.args.get('page', 1)),\n        pages=qr.count() / 20 + 1)\n    kwargs[var_name] = qr.paginate(kwargs['page'])\n    return render_template(template_name, **kwargs)\n\n# retrieve a single object matching the specified query or 404 -- this uses the\n# shortcut \"get\" method on model, which retrieves a single object or raises a\n# DoesNotExist exception if no matching object exists\n# https://charlesleifer.com/docs/peewee/peewee/models.html#Model.get)\ndef get_object_or_404(model, *expressions):\n    try:\n        return model.get(*expressions)\n    except model.DoesNotExist:\n        abort(404)\n\n# custom template filter -- flask allows you to define these functions and then\n# they are accessible in the template -- this one returns a boolean whether the\n# given user is following another user.\n@app.template_filter('is_following')\ndef is_following(from_user, to_user):\n    return from_user.is_following(to_user)\n\n# Request handlers -- these two hooks are provided by flask and we will use them\n# to create and tear down a database connection on each request.\n@app.before_request\ndef before_request():\n    database.connect()\n\n@app.teardown_request\ndef teardown_request(exc=None):\n    if not database.is_closed():\n        database.close()\n\n# views -- these are the actual mappings of url to view function\n@app.route('/')\ndef homepage():\n    # depending on whether the requesting user is logged in or not, show them\n    # either the public timeline or their own private timeline\n    if session.get('logged_in'):\n        return private_timeline()\n    else:\n        return public_timeline()\n\n@app.route('/private/')\ndef private_timeline():\n    # the private timeline exemplifies the use of a subquery -- we are asking for\n    # messages where the person who created the message is someone the current\n    # user is following.  these messages are then ordered newest-first.\n    user = get_current_user()\n    messages = (Message\n                .select()\n                .where(Message.user << user.following())\n                .order_by(Message.pub_date.desc()))\n    return object_list('private_messages.html', messages, 'message_list')\n\n@app.route('/public/')\ndef public_timeline():\n    # simply display all messages, newest first\n    messages = Message.select().order_by(Message.pub_date.desc())\n    return object_list('public_messages.html', messages, 'message_list')\n\n@app.route('/join/', methods=['GET', 'POST'])\ndef join():\n    if request.method == 'POST' and request.form['username']:\n        try:\n            with database.atomic():\n                # Attempt to create the user. If the username is taken, due to the\n                # unique constraint, the database will raise an IntegrityError.\n                user = User.create(\n                    username=request.form['username'],\n                    password=md5((request.form['password']).encode('utf-8')).hexdigest(),\n                    email=request.form['email'],\n                    join_date=datetime.datetime.now())\n\n            # mark the user as being 'authenticated' by setting the session vars\n            auth_user(user)\n            return redirect(url_for('homepage'))\n\n        except IntegrityError:\n            flash('That username is already taken')\n\n    return render_template('join.html')\n\n@app.route('/login/', methods=['GET', 'POST'])\ndef login():\n    if request.method == 'POST' and request.form['username']:\n        try:\n            pw_hash = md5(request.form['password'].encode('utf-8')).hexdigest()\n            user = User.get(\n                (User.username == request.form['username']) &\n                (User.password == pw_hash))\n        except User.DoesNotExist:\n            flash('The password entered is incorrect')\n        else:\n            auth_user(user)\n            return redirect(url_for('homepage'))\n\n    return render_template('login.html')\n\n@app.route('/logout/')\ndef logout():\n    session.pop('logged_in', None)\n    flash('You were logged out')\n    return redirect(url_for('homepage'))\n\n@app.route('/following/')\n@login_required\ndef following():\n    user = get_current_user()\n    return object_list('user_following.html', user.following(), 'user_list')\n\n@app.route('/followers/')\n@login_required\ndef followers():\n    user = get_current_user()\n    return object_list('user_followers.html', user.followers(), 'user_list')\n\n@app.route('/users/')\ndef user_list():\n    users = User.select().order_by(User.username)\n    return object_list('user_list.html', users, 'user_list')\n\n@app.route('/users/<username>/')\ndef user_detail(username):\n    # using the \"get_object_or_404\" shortcut here to get a user with a valid\n    # username or short-circuit and display a 404 if no user exists in the db\n    user = get_object_or_404(User, User.username == username)\n\n    # get all the users messages ordered newest-first -- note how we're accessing\n    # the messages -- user.message_set.  could also have written it as:\n    # Message.select().where(Message.user == user)\n    messages = user.messages.order_by(Message.pub_date.desc())\n    return object_list('user_detail.html', messages, 'message_list', user=user)\n\n@app.route('/users/<username>/follow/', methods=['POST'])\n@login_required\ndef user_follow(username):\n    user = get_object_or_404(User, User.username == username)\n    try:\n        with database.atomic():\n            Relationship.create(\n                from_user=get_current_user(),\n                to_user=user)\n    except IntegrityError:\n        pass\n\n    flash('You are following %s' % user.username)\n    return redirect(url_for('user_detail', username=user.username))\n\n@app.route('/users/<username>/unfollow/', methods=['POST'])\n@login_required\ndef user_unfollow(username):\n    user = get_object_or_404(User, User.username == username)\n    (Relationship\n     .delete()\n     .where(\n         (Relationship.from_user == get_current_user()) &\n         (Relationship.to_user == user))\n     .execute())\n    flash('You are no longer following %s' % user.username)\n    return redirect(url_for('user_detail', username=user.username))\n\n@app.route('/create/', methods=['GET', 'POST'])\n@login_required\ndef create():\n    user = get_current_user()\n    if request.method == 'POST' and request.form['content']:\n        message = Message.create(\n            user=user,\n            content=request.form['content'],\n            pub_date=datetime.datetime.now())\n        flash('Your message has been created')\n        return redirect(url_for('user_detail', username=user.username))\n\n    return render_template('create.html')\n\n@app.context_processor\ndef _inject_user():\n    return {'current_user': get_current_user()}\n\n# allow running from the command line\nif __name__ == '__main__':\n    create_tables()\n    app.run()\n"
  },
  {
    "path": "examples/twitter/requirements.txt",
    "content": "flask\npeewee\n"
  },
  {
    "path": "examples/twitter/run_example.py",
    "content": "#!/usr/bin/env python\n\nimport sys\nsys.path.insert(0, '../..')\n\nfrom app import app, create_tables\ncreate_tables()\napp.run()\n"
  },
  {
    "path": "examples/twitter/static/style.css",
    "content": "body            { font-family: sans-serif; background: #eee; }\na, h1, h2       { color: #377BA8; }\nh1, h2          { font-family: 'Georgia', serif; margin: 0; }\nh1              { border-bottom: 2px solid #eee; }\nh2              { font-size: 1.2em; }\n\n.page           { margin: 2em auto; width: 35em; border: 5px solid #ccc;\n                  padding: 0.8em; background: white; }\n.page ul        { list-style-type: none; }\n.page li        { clear: both; }\n.metanav        { text-align: right; font-size: 0.8em; padding: 0.3em;\n                  margin-bottom: 1em; background: #fafafa; }\n.flash          { background: #CEE5F5; padding: 0.5em;\n                  border: 1px solid #AACBE2; }\n.avatar         { display: block; float: left; margin: 0 10px 0 0; }\n.message-content { min-height: 80px; }\n"
  },
  {
    "path": "examples/twitter/templates/create.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Create</h2>\n  <form action=\"{{ url_for('create') }}\" method=post>\n    <dl>\n      <dt>Message:</dt>\n      <dd><textarea name=\"content\"></textarea></dd>\n      <dd><input type=\"submit\" value=\"Create\" /></dd>\n    </dl>\n  </form>\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/homepage.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Home</h2>\n  <p>Welcome to the site!</p>\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/includes/message.html",
    "content": "<a class=\"avatar\" href=\"{{ url_for('user_detail', username=message.user.username) }}\"><img src=\"{{ message.user.gravatar_url() }}\" /></a>\n<p class=\"message-content\">{{ message.content|urlize }}</p>\n"
  },
  {
    "path": "examples/twitter/templates/includes/pagination.html",
    "content": "{% if page > 1 %}\n  <a class=\"prev\" href=\"?page={{ page - 1 }}\">Previous</a>\n{% endif %}\n{% if page < pages %}\n  <a class=\"next\" href=\"?page={{ page + 1 }}\">Next</a>\n{% endif %}\n"
  },
  {
    "path": "examples/twitter/templates/join.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Join</h2>\n  <form action=\"{{ url_for('join') }}\" method=\"post\">\n    <dl>\n      <dt>Username:</dt>\n      <dd><input type=\"text\" name=\"username\"></dd>\n      <dt>Password:</dt>\n      <dd><input type=\"password\" name=\"password\"></dd>\n      <dt>Email:</dt>\n      <dd><input type=\"text\" name=\"email\">\n        <p><small>(used for gravatar)</small></p>\n      </dd>\n      <dd><input type=\"submit\" value=\"Join\">\n    </dl>\n  </form>\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/layout.html",
    "content": "<!doctype html>\n<title>Tweepee</title>\n<link rel=stylesheet type=text/css href=\"{{ url_for('static', filename='style.css') }}\">\n<div class=page>\n  <h1><a href=\"{{ url_for('homepage') }}\">Tweepee</a></h1>\n  <div class=metanav>\n  {% if not session.logged_in %}\n    <a href=\"{{ url_for('login') }}\">log in</a>\n    <a href=\"{{ url_for('join') }}\">join</a>\n  {% else %}\n    <a href=\"{{ url_for('public_timeline') }}\">public timeline</a>\n    <a href=\"{{ url_for('create') }}\">create</a>\n    <a href=\"{{ url_for('logout') }}\">log out</a>\n  {% endif %}\n  </div>\n  {% for message in get_flashed_messages() %}\n    <div class=flash>{{ message }}</div>\n  {% endfor %}\n  {% block body %}{% endblock %}\n</div>\n"
  },
  {
    "path": "examples/twitter/templates/login.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Login</h2>\n  {% if error %}<p class=error><strong>Error:</strong> {{ error }}{% endif %}\n  <form action=\"{{ url_for('login') }}\" method=post>\n    <dl>\n      <dt>Username:\n      <dd><input type=text name=username>\n      <dt>Password:\n      <dd><input type=password name=password>\n      <dd><input type=submit value=Login>\n    </dl>\n  </form>\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/private_messages.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Private Timeline</h2>\n  <ul>\n    {% for message in message_list %}\n      <li>{% include \"includes/message.html\" %}</li>\n    {% endfor %}\n  </ul>\n  {% include \"includes/pagination.html\" %}\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/public_messages.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Public Timeline</h2>\n  <ul>\n    {% for message in message_list %}\n      <li>{% include \"includes/message.html\" %}</li>\n    {% endfor %}\n  </ul>\n  {% include \"includes/pagination.html\" %}\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/user_detail.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Messages from {{ user.username }}</h2>\n  {% if current_user %}\n    {% if user.username != current_user.username %}\n      {% if current_user|is_following(user) %}\n        <form action=\"{{ url_for('user_unfollow', username=user.username) }}\" method=\"post\">\n          <input type=\"submit\" value=\"- Un-follow\" />\n        </form>\n      {% else %}\n        <form action=\"{{ url_for('user_follow', username=user.username) }}\" method=\"post\">\n          <input type=\"submit\" value=\"+ Follow\" />\n        </form>\n      {% endif %}\n    {% endif %}\n  {% endif %}\n  <ul>\n    {% for message in message_list %}\n      <li>{% include \"includes/message.html\" %}</li>\n    {% endfor %}\n  </ul>\n  {% include \"includes/pagination.html\" %}\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/user_followers.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Followers</h2>\n  <ul>\n    {% for user in current_user.followers() %}\n      <li><a href=\"{{ url_for('user_detail', username=user.username) }}\">{{ user.username }}</a></li>\n    {% endfor %}\n  </ul>\n  {% include \"includes/pagination.html\" %}\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/user_following.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Following</h2>\n  <ul>\n    {% for user in current_user.following() %}\n      <li><a href=\"{{ url_for('user_detail', username=user.username) }}\">{{ user.username }}</a></li>\n    {% endfor %}\n  </ul>\n  {% include \"includes/pagination.html\" %}\n{% endblock %}\n"
  },
  {
    "path": "examples/twitter/templates/user_list.html",
    "content": "{% extends \"layout.html\" %}\n{% block body %}\n  <h2>Users</h2>\n  <ul>\n    {% for user in user_list %}\n      <li><a href=\"{{ url_for('user_detail', username=user.username) }}\">{{ user.username }}</a></li>\n    {% endfor %}\n  </ul>\n  {% include \"includes/pagination.html\" %}\n{% endblock %}\n"
  },
  {
    "path": "peewee.py",
    "content": "from bisect import bisect_left\nfrom bisect import bisect_right\nfrom collections.abc import Callable\nfrom collections.abc import Mapping\nfrom contextlib import contextmanager\nfrom copy import deepcopy\nfrom functools import reduce\nfrom functools import wraps\nfrom inspect import isclass\nimport calendar\nimport collections\nimport datetime\nimport decimal\nimport hashlib\nimport itertools\nimport logging\nimport operator\nimport re\nimport socket\nimport struct\nimport sys\nimport threading\nimport time\nimport types\nimport uuid\nimport warnings\n\ntry:\n    from pysqlite3 import dbapi2 as pysq3\nexcept ImportError:\n    pysq3 = None\ntry:\n    import sqlite3\nexcept ImportError:\n    sqlite3 = pysq3\nelse:\n    if pysq3 and pysq3.sqlite_version_info >= sqlite3.sqlite_version_info:\n        sqlite3 = pysq3\n\ntry:\n    from psycopg2cffi import compat\n    compat.register()\nexcept ImportError:\n    pass\ntry:\n    import psycopg2\n    from psycopg2 import errors as pg_errors\n    from psycopg2 import extensions as pg_extensions\n    from psycopg2.extras import register_uuid as pg_register_uuid\n    from psycopg2.extras import Json as Json_pg2\n    pg_register_uuid()\nexcept ImportError:\n    psycopg2 = pg_errors = Json_pg2 = None\ntry:\n    import psycopg\n    from psycopg import errors as pg3_errors\n    from psycopg.pq import TransactionStatus\n    from psycopg.types.json import Json as Json_pg3\n    from psycopg.types.json import Jsonb as Jsonb_pg3\nexcept ImportError:\n    psycopg = pg3_errors = Json_pg3 = Jsonb_pg3 = None\n\nmysql_passwd = False\ntry:\n    import pymysql as mysql\nexcept ImportError:\n    try:\n        import MySQLdb as mysql\n        mysql_passwd = True\n    except ImportError:\n        mysql = None\n\n\n__version__ = '4.0.2'\n__all__ = [\n    'AnyField',\n    'AsIs',\n    'AutoField',\n    'BareField',\n    'BigAutoField',\n    'BigBitField',\n    'BigIntegerField',\n    'BinaryUUIDField',\n    'BitField',\n    'BlobField',\n    'BooleanField',\n    'Case',\n    'Cast',\n    'CharField',\n    'Check',\n    'chunked',\n    'Column',\n    'CompositeKey',\n    'Context',\n    'Database',\n    'DatabaseError',\n    'DatabaseProxy',\n    'DataError',\n    'DateField',\n    'DateTimeField',\n    'DecimalField',\n    'Default',\n    'DeferredForeignKey',\n    'DeferredThroughModel',\n    'DJANGO_MAP',\n    'DoesNotExist',\n    'DoubleField',\n    'DQ',\n    'EXCLUDED',\n    'Field',\n    'FixedCharField',\n    'FloatField',\n    'fn',\n    'ForeignKeyField',\n    'IdentityField',\n    'ImproperlyConfigured',\n    'Index',\n    'IntegerField',\n    'IntegrityError',\n    'InterfaceError',\n    'InternalError',\n    'IPField',\n    'JOIN',\n    'ManyToManyField',\n    'Model',\n    'ModelIndex',\n    'MySQLDatabase',\n    'NotSupportedError',\n    'OP',\n    'OperationalError',\n    'PostgresqlDatabase',\n    'PrimaryKeyField',  # XXX: Deprecated, change to AutoField.\n    'prefetch',\n    'PREFETCH_TYPE',\n    'ProgrammingError',\n    'Proxy',\n    'QualifiedNames',\n    'SchemaManager',\n    'SmallIntegerField',\n    'Select',\n    'SQL',\n    'SqliteDatabase',\n    'Table',\n    'TextField',\n    'TimeField',\n    'TimestampField',\n    'Tuple',\n    'UUIDField',\n    'Value',\n    'ValuesList',\n    'Window',\n]\n\nlogger = logging.getLogger('peewee')\nlogger.addHandler(logging.NullHandler())\n\n\ncallable_ = lambda c: isinstance(c, Callable)\nmulti_types = (list, tuple, frozenset, set, range, types.GeneratorType)\n\ndef reraise(tp, value, tb=None):\n    if value.__traceback__ is not tb:\n        raise value.with_traceback(tb)\n    raise value\n\n# Other compat issues.\nif sys.version_info < (3, 12):\n    utcfromtimestamp = datetime.datetime.utcfromtimestamp\n    utcnow = datetime.datetime.utcnow\nelse:\n    def utcfromtimestamp(ts):\n        return (datetime.datetime\n                .fromtimestamp(ts, tz=datetime.timezone.utc)\n                .replace(tzinfo=None))\n    def utcnow():\n        return (datetime.datetime\n                .now(datetime.timezone.utc)\n                .replace(tzinfo=None))\n\n\nif sqlite3:\n    sqlite3.register_adapter(decimal.Decimal, str)\n    sqlite3.register_adapter(datetime.date, str)\n    sqlite3.register_adapter(datetime.time, str)\n    if sys.version_info >= (3, 12):\n        # We need to register datetime adapters as these are deprecated.\n        def datetime_adapter(d): return d.isoformat(' ')\n        def convert_date(d): return datetime.date(*map(int, d.split(b'-')))\n        def convert_timestamp(t):\n            date, time = t.split(b'T') if b'T' in t else t.split(b' ')\n            y, m, d = map(int, date.split(b'-'))\n            t_full = time.split(b'.')\n            hour, minute, second = map(int, t_full[0].split(b':'))\n            if len(t_full) == 2:\n                usec = int('{:0<6.6}'.format(t_full[1].decode()))\n            else:\n                usec = 0\n            return datetime.datetime(y, m, d, hour, minute, second, usec)\n        sqlite3.register_adapter(datetime.datetime, datetime_adapter)\n        sqlite3.register_converter('date', convert_date)\n        sqlite3.register_converter('timestamp', convert_timestamp)\n\n    __sqlite_version__ = sqlite3.sqlite_version_info\nelse:\n    __sqlite_version__ = (0, 0, 0)\n\n\n__date_parts__ = set(('year', 'month', 'day', 'hour', 'minute', 'second'))\n\n# Sqlite does not support the `date_part` SQL function, so we will define an\n# implementation in python.\n__sqlite_datetime_formats__ = (\n    '%Y-%m-%d %H:%M:%S',\n    '%Y-%m-%d %H:%M:%S.%f',\n    '%Y-%m-%d',\n    '%H:%M:%S',\n    '%H:%M:%S.%f',\n    '%H:%M')\n\n__sqlite_date_trunc__ = {\n    'year': '%Y-01-01 00:00:00',\n    'month': '%Y-%m-01 00:00:00',\n    'day': '%Y-%m-%d 00:00:00',\n    'hour': '%Y-%m-%d %H:00:00',\n    'minute': '%Y-%m-%d %H:%M:00',\n    'second': '%Y-%m-%d %H:%M:%S'}\n\n__mysql_date_trunc__ = __sqlite_date_trunc__.copy()\n__mysql_date_trunc__['minute'] = '%Y-%m-%d %H:%i:00'\n__mysql_date_trunc__['second'] = '%Y-%m-%d %H:%i:%S'\n\ndef _sqlite_date_part(lookup_type, datetime_string):\n    assert lookup_type in __date_parts__\n    if not datetime_string:\n        return\n    dt = format_date_time(datetime_string, __sqlite_datetime_formats__)\n    return getattr(dt, lookup_type)\n\ndef _sqlite_date_trunc(lookup_type, datetime_string):\n    assert lookup_type in __sqlite_date_trunc__\n    if not datetime_string:\n        return\n    dt = format_date_time(datetime_string, __sqlite_datetime_formats__)\n    return dt.strftime(__sqlite_date_trunc__[lookup_type])\n\ndef _sqlite_regexp(regex, value):\n    if value is None:\n        return False\n    return re.search(regex, value) is not None\n\n\ndef __deprecated__(s):\n    warnings.warn(s, DeprecationWarning)\n\n\nclass attrdict(dict):\n    def __getattr__(self, attr):\n        try:\n            return self[attr]\n        except KeyError:\n            raise AttributeError(attr)\n    def __setattr__(self, attr, value): self[attr] = value\n    def __iadd__(self, rhs): self.update(rhs); return self\n    def __add__(self, rhs): d = attrdict(self); d.update(rhs); return d\n\nSENTINEL = object()\n\n#: Operations for use in SQL expressions.\nOP = attrdict(\n    AND='AND',\n    OR='OR',\n    ADD='+',\n    SUB='-',\n    MUL='*',\n    DIV='/',\n    BIN_AND='&',\n    BIN_OR='|',\n    XOR='#',\n    MOD='%',\n    EQ='=',\n    LT='<',\n    LTE='<=',\n    GT='>',\n    GTE='>=',\n    NE='!=',\n    IN='IN',\n    NOT_IN='NOT IN',\n    IS='IS',\n    IS_NOT='IS NOT',\n    LIKE='LIKE',\n    ILIKE='ILIKE',\n    BETWEEN='BETWEEN',\n    REGEXP='REGEXP',\n    IREGEXP='IREGEXP',\n    CONCAT='||',\n    BITWISE_NEGATION='~')\n\n# To support \"django-style\" double-underscore filters, create a mapping between\n# operation name and operation code, e.g. \"__eq\" == OP.EQ.\nDJANGO_MAP = attrdict({\n    'eq': operator.eq,\n    'lt': operator.lt,\n    'lte': operator.le,\n    'gt': operator.gt,\n    'gte': operator.ge,\n    'ne': operator.ne,\n    'in': operator.lshift,\n    'is': lambda l, r: Expression(l, OP.IS, r),\n    'is_not': lambda l, r: Expression(l, OP.IS_NOT, r),\n    'like': lambda l, r: Expression(l, OP.LIKE, r),\n    'ilike': lambda l, r: Expression(l, OP.ILIKE, r),\n    'regexp': lambda l, r: Expression(l, OP.REGEXP, r),\n})\n\n#: Mapping of field type to the data-type supported by the database. Databases\n#: may override or add to this list.\nFIELD = attrdict(\n    AUTO='INTEGER',\n    BIGAUTO='BIGINT',\n    BIGINT='BIGINT',\n    BLOB='BLOB',\n    BOOL='SMALLINT',\n    CHAR='CHAR',\n    DATE='DATE',\n    DATETIME='DATETIME',\n    DECIMAL='DECIMAL',\n    DEFAULT='',\n    DOUBLE='REAL',\n    FLOAT='REAL',\n    INT='INTEGER',\n    SMALLINT='SMALLINT',\n    TEXT='TEXT',\n    TIME='TIME',\n    UUID='TEXT',\n    UUIDB='BLOB',\n    VARCHAR='VARCHAR')\n\n#: Join helpers (for convenience) -- all join types are supported, this object\n#: is just to help avoid introducing errors by using strings everywhere.\nJOIN = attrdict(\n    INNER='INNER JOIN',\n    LEFT_OUTER='LEFT OUTER JOIN',\n    RIGHT_OUTER='RIGHT OUTER JOIN',\n    FULL='FULL JOIN',\n    FULL_OUTER='FULL OUTER JOIN',\n    CROSS='CROSS JOIN',\n    NATURAL='NATURAL JOIN',\n    LATERAL='LATERAL',\n    LEFT_LATERAL='LEFT JOIN LATERAL')\n\n# Row representations.\nROW = attrdict(\n    TUPLE=1,\n    DICT=2,\n    NAMED_TUPLE=3,\n    CONSTRUCTOR=4,\n    MODEL=5)\n\n# Query type to use with prefetch\nPREFETCH_TYPE = attrdict(\n    WHERE=1,\n    JOIN=2)\n\nSCOPE_NORMAL = 1\nSCOPE_SOURCE = 2\nSCOPE_VALUES = 4\nSCOPE_CTE = 8\nSCOPE_COLUMN = 16\n\n# Rules for parentheses around subqueries in compound select.\nCSQ_PARENTHESES_NEVER = 0\nCSQ_PARENTHESES_ALWAYS = 1\nCSQ_PARENTHESES_UNNESTED = 2\n\n# Regular expressions used to convert class names to snake-case table names.\n# First regex handles acronym followed by word or initial lower-word followed\n# by a capitalized word. e.g. APIResponse -> API_Response / fooBar -> foo_Bar.\n# Second regex handles the normal case of two title-cased words.\nSNAKE_CASE_STEP1 = re.compile('(.)_*([A-Z][a-z]+)')\nSNAKE_CASE_STEP2 = re.compile('([a-z0-9])_*([A-Z])')\n\n# Used for making valid Python identifiers.\nIDENTIFIER_RE = re.compile(r'[A-Za-z_][A-Za-z0-9_]*')\n\n# Helper functions that are used in various parts of the codebase.\nMODEL_BASE = '_metaclass_helper_'\n\ndef with_metaclass(meta, base=object):\n    return meta(MODEL_BASE, (base,), {})\n\ndef merge_dict(source, overrides):\n    merged = source.copy()\n    if overrides:\n        merged.update(overrides)\n    return merged\n\ndef quote(path, quote_chars):\n    if len(path) == 1:\n        return path[0].join(quote_chars)\n    return '.'.join([part.join(quote_chars) for part in path])\n\nis_model = lambda o: isclass(o) and issubclass(o, Model)\n\ndef ensure_tuple(value):\n    if value is not None:\n        return value if isinstance(value, (list, tuple)) else (value,)\n\ndef ensure_entity(value):\n    if value is not None:\n        return value if isinstance(value, Node) else Entity(value)\n\ndef make_snake_case(s):\n    first = SNAKE_CASE_STEP1.sub(r'\\1_\\2', s)\n    return SNAKE_CASE_STEP2.sub(r'\\1_\\2', first).lower()\n\ndef make_identifier(s):\n    match_obj = IDENTIFIER_RE.search(s.rsplit('.', 1)[-1])\n    if match_obj is not None:\n        return match_obj.group()\n    return s\n\ndef chunked(it, n):\n    marker = object()\n    groups = itertools.zip_longest(*[iter(it)] * n, fillvalue=marker)\n    for group in (list(g) for g in groups):\n        while group and group[-1] is marker:\n            group.pop()\n        yield group\n\n\nclass _callable_context_manager(object):\n    def __call__(self, fn):\n        @wraps(fn)\n        def inner(*args, **kwargs):\n            with self:\n                return fn(*args, **kwargs)\n        return inner\n\n\nclass Proxy(object):\n    \"\"\"\n    Create a proxy or placeholder for another object.\n    \"\"\"\n    __slots__ = ('obj', '_callbacks')\n\n    def __init__(self):\n        self._callbacks = []\n        self.initialize(None)\n\n    def initialize(self, obj):\n        self.obj = obj\n        for callback in self._callbacks:\n            callback(obj)\n\n    def attach_callback(self, callback):\n        self._callbacks.append(callback)\n        return callback\n\n    def passthrough(method):\n        def inner(self, *args, **kwargs):\n            if self.obj is None:\n                raise AttributeError('Cannot use uninitialized Proxy.')\n            return getattr(self.obj, method)(*args, **kwargs)\n        return inner\n\n    # Allow proxy to be used as a context-manager.\n    __enter__ = passthrough('__enter__')\n    __exit__ = passthrough('__exit__')\n\n    def __getattr__(self, attr):\n        if self.obj is None:\n            raise AttributeError('Cannot use uninitialized Proxy.')\n        return getattr(self.obj, attr)\n\n    def __setattr__(self, attr, value):\n        if attr not in self.__slots__:\n            raise AttributeError('Cannot set attribute on proxy.')\n        return super(Proxy, self).__setattr__(attr, value)\n\n\nclass DatabaseProxy(Proxy):\n    \"\"\"\n    Proxy implementation specifically for proxying `Database` objects.\n    \"\"\"\n    __slots__ = ('obj', '_callbacks', '_Model')\n\n    def connection_context(self):\n        return ConnectionContext(self)\n    def atomic(self, *args, **kwargs):\n        return _atomic(self, *args, **kwargs)\n    def manual_commit(self):\n        return _manual(self)\n    def transaction(self, *args, **kwargs):\n        return _transaction(self, *args, **kwargs)\n    def savepoint(self):\n        return _savepoint(self)\n    @property\n    def Model(self):\n        if not hasattr(self, '_Model'):\n            class Meta: database = self\n            self._Model = type('BaseModel', (Model,), {'Meta': Meta})\n        return self._Model\n\n\nclass ModelDescriptor(object): pass\n\n\n# SQL Generation.\n\n\nclass AliasManager(object):\n    __slots__ = ('_counter', '_current_index', '_mapping')\n\n    def __init__(self):\n        # A list of dictionaries containing mappings at various depths.\n        self._counter = 0\n        self._current_index = 0\n        self._mapping = []\n        self.push()\n\n    @property\n    def mapping(self):\n        return self._mapping[self._current_index - 1]\n\n    def add(self, source):\n        if source not in self.mapping:\n            self._counter += 1\n            self[source] = 't%d' % self._counter\n        return self.mapping[source]\n\n    def get(self, source, any_depth=False):\n        if any_depth:\n            for idx in reversed(range(self._current_index)):\n                if source in self._mapping[idx]:\n                    return self._mapping[idx][source]\n        return self.add(source)\n\n    def __getitem__(self, source):\n        return self.get(source)\n\n    def __setitem__(self, source, alias):\n        self.mapping[source] = alias\n\n    def push(self):\n        self._current_index += 1\n        if self._current_index > len(self._mapping):\n            self._mapping.append({})\n\n    def pop(self):\n        if self._current_index == 1:\n            raise ValueError('Cannot pop() from empty alias manager.')\n        self._mapping[self._current_index - 1].clear()\n        self._current_index -= 1\n\n\nclass State(collections.namedtuple('_State', ('scope', 'parentheses',\n                                              'settings'))):\n    def __new__(cls, scope=SCOPE_NORMAL, parentheses=False, **kwargs):\n        return super(State, cls).__new__(cls, scope, parentheses, kwargs)\n\n    def __call__(self, scope=None, parentheses=None, **kwargs):\n        # Scope and settings are \"inherited\" (parentheses is not, however).\n        scope = self.scope if scope is None else scope\n\n        # Try to avoid unnecessary dict copying.\n        if kwargs and self.settings:\n            settings = self.settings.copy()  # Copy original settings dict.\n            settings.update(kwargs)  # Update copy with overrides.\n        elif kwargs:\n            settings = kwargs\n        else:\n            settings = self.settings\n        return State(scope, parentheses, **settings)\n\n    def __getattr__(self, attr_name):\n        return self.settings.get(attr_name)\n\n\ndef __scope_context__(scope):\n    @contextmanager\n    def inner(self, **kwargs):\n        with self(scope=scope, **kwargs):\n            yield self\n    return inner\n\n\nclass Context(object):\n    __slots__ = ('stack', '_sql', '_values', 'alias_manager', 'state')\n\n    def __init__(self, **settings):\n        self.stack = []\n        self._sql = []\n        self._values = []\n        self.alias_manager = AliasManager()\n        self.state = State(**settings)\n\n    def as_new(self):\n        return Context(**self.state.settings)\n\n    def column_sort_key(self, item):\n        return item[0].get_sort_key(self)\n\n    @property\n    def scope(self):\n        return self.state.scope\n\n    @property\n    def parentheses(self):\n        return self.state.parentheses\n\n    @property\n    def subquery(self):\n        return self.state.subquery\n\n    def __call__(self, **overrides):\n        if overrides and overrides.get('scope') == self.scope:\n            del overrides['scope']\n\n        self.stack.append(self.state)\n        self.state = self.state(**overrides)\n        return self\n\n    scope_normal = __scope_context__(SCOPE_NORMAL)\n    scope_source = __scope_context__(SCOPE_SOURCE)\n    scope_values = __scope_context__(SCOPE_VALUES)\n    scope_cte = __scope_context__(SCOPE_CTE)\n    scope_column = __scope_context__(SCOPE_COLUMN)\n\n    def __enter__(self):\n        if self.parentheses:\n            self.literal('(')\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if self.parentheses:\n            self.literal(')')\n        self.state = self.stack.pop()\n\n    @contextmanager\n    def push_alias(self):\n        self.alias_manager.push()\n        yield\n        self.alias_manager.pop()\n\n    def sql(self, obj):\n        if isinstance(obj, (Node, Context)):\n            return obj.__sql__(self)\n        elif is_model(obj):\n            return obj._meta.table.__sql__(self)\n        else:\n            return self.sql(Value(obj))\n\n    def literal(self, keyword):\n        self._sql.append(keyword)\n        return self\n\n    def value(self, value, converter=None, add_param=True):\n        if converter:\n            value = converter(value)\n        elif converter is None and self.state.converter:\n            # Explicitly check for None so that \"False\" can be used to signify\n            # that no conversion should be applied.\n            value = self.state.converter(value)\n\n        if isinstance(value, Node):\n            with self(converter=None):\n                return self.sql(value)\n        elif is_model(value):\n            # Under certain circumstances, we could end-up treating a model-\n            # class itself as a value. This check ensures that we drop the\n            # table alias into the query instead of trying to parameterize a\n            # model (for instance, passing a model as a function argument).\n            with self.scope_column():\n                return self.sql(value)\n\n        if self.state.value_literals:\n            return self.literal(_query_val_transform(value))\n\n        self._values.append(value)\n        return self.literal(self.state.param or '?') if add_param else self\n\n    def __sql__(self, ctx):\n        ctx._sql.extend(self._sql)\n        ctx._values.extend(self._values)\n        return ctx\n\n    def parse(self, node):\n        return self.sql(node).query()\n\n    def query(self):\n        return ''.join(self._sql), self._values\n\n\ndef query_to_string(query):\n    # NOTE: this function is not exported by default as it might be misused --\n    # and this misuse could lead to sql injection vulnerabilities. This\n    # function is intended for debugging or logging purposes ONLY.\n    db = getattr(query, '_database', None)\n    if db is not None:\n        ctx = db.get_sql_context()\n    else:\n        ctx = Context()\n\n    sql, params = ctx.sql(query).query()\n    if not params:\n        return sql\n\n    param = ctx.state.param or '?'\n    if param == '?':\n        sql = sql.replace('?', '%s')\n\n    return sql % tuple(map(_query_val_transform, params))\n\ndef _query_val_transform(v):\n    # Interpolate parameters.\n    if isinstance(v, (str, datetime.datetime, datetime.date,\n                      datetime.time)):\n        v = \"'%s'\" % str(v).replace(\"'\", \"''\")\n    elif isinstance(v, bytes):\n        try:\n            v = v.decode('utf8')\n        except UnicodeDecodeError:\n            v = v.decode('raw_unicode_escape')\n        v = \"'%s'\" % v.replace(\"'\", \"''\")\n    elif isinstance(v, int):\n        v = '%s' % int(v)  # Also handles booleans -> 1 or 0.\n    elif v is None:\n        v = 'NULL'\n    else:\n        v = str(v)\n    return v\n\n\n# AST.\n\n\nclass Node(object):\n    _coerce = True\n    __isabstractmethod__ = False  # Avoid issue w/abc and __getattr__, eg fn.X\n\n    def clone(self):\n        obj = self.__class__.__new__(self.__class__)\n        obj.__dict__ = self.__dict__.copy()\n        return obj\n\n    def __sql__(self, ctx):\n        raise NotImplementedError\n\n    @staticmethod\n    def copy(method):\n        def inner(self, *args, **kwargs):\n            clone = self.clone()\n            method(clone, *args, **kwargs)\n            return clone\n        return inner\n\n    def coerce(self, _coerce=True):\n        if _coerce != self._coerce:\n            clone = self.clone()\n            clone._coerce = _coerce\n            return clone\n        return self\n\n    def is_alias(self):\n        return False\n\n    def unwrap(self):\n        return self\n\n\nclass ColumnFactory(object):\n    __slots__ = ('node',)\n\n    def __init__(self, node):\n        self.node = node\n\n    def __getattr__(self, attr):\n        return Column(self.node, attr)\n    __getitem__ = __getattr__\n\n\nclass _DynamicColumn(object):\n    __slots__ = ()\n\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            return ColumnFactory(instance)  # Implements __getattr__().\n        return self\n\n\nclass _ExplicitColumn(object):\n    __slots__ = ()\n\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            raise AttributeError(\n                '%s specifies columns explicitly, and does not support '\n                'dynamic column lookups.' % instance)\n        return self\n\n\nclass Star(Node):\n    def __init__(self, source):\n        self.source = source\n    def __sql__(self, ctx):\n        return ctx.sql(QualifiedNames(self.source)).literal('.*')\n\n\nclass Source(Node):\n    c = _DynamicColumn()\n\n    def __init__(self, alias=None):\n        super(Source, self).__init__()\n        self._alias = alias\n\n    @Node.copy\n    def alias(self, name):\n        self._alias = name\n\n    def select(self, *columns):\n        if not columns:\n            columns = (SQL('*'),)\n        return Select((self,), columns)\n\n    @property\n    def __star__(self):\n        return Star(self)\n\n    def join(self, dest, join_type=JOIN.INNER, on=None):\n        return Join(self, dest, join_type, on)\n\n    def left_outer_join(self, dest, on=None):\n        return Join(self, dest, JOIN.LEFT_OUTER, on)\n\n    def cte(self, name, recursive=False, columns=None, materialized=None):\n        return CTE(name, self, recursive=recursive, columns=columns,\n                   materialized=materialized)\n\n    def get_sort_key(self, ctx):\n        if self._alias:\n            return (self._alias,)\n        return (ctx.alias_manager[self],)\n\n    def apply_alias(self, ctx):\n        # If we are defining the source, include the \"AS alias\" declaration. An\n        # alias is created for the source if one is not already defined.\n        if ctx.scope == SCOPE_SOURCE:\n            if self._alias:\n                ctx.alias_manager[self] = self._alias\n            ctx.literal(' AS ').sql(Entity(ctx.alias_manager[self]))\n        return ctx\n\n    def apply_column(self, ctx):\n        if self._alias:\n            ctx.alias_manager[self] = self._alias\n        return ctx.sql(Entity(ctx.alias_manager[self]))\n\n\nclass _HashableSource(object):\n    def __init__(self, *args, **kwargs):\n        super(_HashableSource, self).__init__(*args, **kwargs)\n        self._update_hash()\n\n    @Node.copy\n    def alias(self, name):\n        self._alias = name\n        self._update_hash()\n\n    def _update_hash(self):\n        self._hash = self._get_hash()\n\n    def _get_hash(self):\n        return hash((self.__class__, self._path, self._alias))\n\n    def __hash__(self):\n        return self._hash\n\n    def __eq__(self, other):\n        if isinstance(other, _HashableSource):\n            return self._hash == other._hash\n        return Expression(self, OP.EQ, other)\n\n    def __ne__(self, other):\n        if isinstance(other, _HashableSource):\n            return self._hash != other._hash\n        return Expression(self, OP.NE, other)\n\n    def _e(op):\n        def inner(self, rhs):\n            return Expression(self, op, rhs)\n        return inner\n    __lt__ = _e(OP.LT)\n    __le__ = _e(OP.LTE)\n    __gt__ = _e(OP.GT)\n    __ge__ = _e(OP.GTE)\n\n\ndef __bind_database__(meth):\n    @wraps(meth)\n    def inner(self, *args, **kwargs):\n        result = meth(self, *args, **kwargs)\n        if self._database:\n            return result.bind(self._database)\n        return result\n    return inner\n\n\ndef __join__(join_type=JOIN.INNER, inverted=False):\n    def method(self, other):\n        if inverted:\n            self, other = other, self\n        return Join(self, other, join_type=join_type)\n    return method\n\n\nclass BaseTable(Source):\n    __and__ = __join__(JOIN.INNER)\n    __add__ = __join__(JOIN.LEFT_OUTER)\n    __sub__ = __join__(JOIN.RIGHT_OUTER)\n    __or__ = __join__(JOIN.FULL_OUTER)\n    __mul__ = __join__(JOIN.CROSS)\n    __rand__ = __join__(JOIN.INNER, inverted=True)\n    __radd__ = __join__(JOIN.LEFT_OUTER, inverted=True)\n    __rsub__ = __join__(JOIN.RIGHT_OUTER, inverted=True)\n    __ror__ = __join__(JOIN.FULL_OUTER, inverted=True)\n    __rmul__ = __join__(JOIN.CROSS, inverted=True)\n\n\nclass _BoundTableContext(object):\n    def __init__(self, table, database):\n        self.table = table\n        self.database = database\n\n    def __call__(self, fn):\n        @wraps(fn)\n        def inner(*args, **kwargs):\n            with _BoundTableContext(self.table, self.database):\n                return fn(*args, **kwargs)\n        return inner\n\n    def __enter__(self):\n        self._orig_database = self.table._database\n        self.table.bind(self.database)\n        if self.table._model is not None:\n            self.table._model.bind(self.database)\n        return self.table\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.table.bind(self._orig_database)\n        if self.table._model is not None:\n            self.table._model.bind(self._orig_database)\n\n\nclass Table(_HashableSource, BaseTable):\n    def __init__(self, name, columns=None, primary_key=None, schema=None,\n                 alias=None, _model=None, _database=None):\n        self.__name__ = name\n        self._columns = columns\n        self._primary_key = primary_key\n        self._schema = schema\n        self._path = (schema, name) if schema else (name,)\n        self._model = _model\n        self._database = _database\n        super(Table, self).__init__(alias=alias)\n\n        # Allow tables to restrict what columns are available.\n        if columns is not None:\n            self.c = _ExplicitColumn()\n            for column in columns:\n                setattr(self, column, Column(self, column))\n\n        if primary_key:\n            col_src = self if self._columns else self.c\n            self.primary_key = getattr(col_src, primary_key)\n        else:\n            self.primary_key = None\n\n    def clone(self):\n        # Ensure a deep copy of the column instances.\n        return Table(\n            self.__name__,\n            columns=self._columns,\n            primary_key=self._primary_key,\n            schema=self._schema,\n            alias=self._alias,\n            _model=self._model,\n            _database=self._database)\n\n    def bind(self, database=None):\n        self._database = database\n        return self\n\n    def bind_ctx(self, database=None):\n        return _BoundTableContext(self, database)\n\n    def _get_hash(self):\n        return hash((self.__class__, self._path, self._alias, self._model))\n\n    @__bind_database__\n    def select(self, *columns):\n        if not columns and self._columns:\n            columns = [Column(self, column) for column in self._columns]\n        return Select((self,), columns)\n\n    @__bind_database__\n    def insert(self, insert=None, columns=None, **kwargs):\n        if kwargs:\n            insert = {} if insert is None else insert\n            src = self if self._columns else self.c\n            for key, value in kwargs.items():\n                insert[getattr(src, key)] = value\n        return Insert(self, insert=insert, columns=columns)\n\n    @__bind_database__\n    def replace(self, insert=None, columns=None, **kwargs):\n        return (self\n                .insert(insert=insert, columns=columns)\n                .on_conflict('REPLACE'))\n\n    @__bind_database__\n    def update(self, update=None, **kwargs):\n        if kwargs:\n            update = {} if update is None else update\n            for key, value in kwargs.items():\n                src = self if self._columns else self.c\n                update[getattr(src, key)] = value\n        return Update(self, update=update)\n\n    @__bind_database__\n    def delete(self):\n        return Delete(self)\n\n    def __sql__(self, ctx):\n        if ctx.scope == SCOPE_VALUES:\n            # Return the quoted table name.\n            return ctx.sql(Entity(*self._path))\n\n        if self._alias:\n            ctx.alias_manager[self] = self._alias\n\n        if ctx.scope == SCOPE_SOURCE:\n            # Define the table and its alias.\n            return self.apply_alias(ctx.sql(Entity(*self._path)))\n        else:\n            # Refer to the table using the alias.\n            return self.apply_column(ctx)\n\n\nclass Join(BaseTable):\n    def __init__(self, lhs, rhs, join_type=JOIN.INNER, on=None, alias=None):\n        super(Join, self).__init__(alias=alias)\n        self.lhs = lhs\n        self.rhs = rhs\n        self.join_type = join_type\n        self._on = on\n\n    def on(self, predicate):\n        self._on = predicate\n        return self\n\n    def __sql__(self, ctx):\n        (ctx\n         .sql(self.lhs)\n         .literal(' %s ' % self.join_type)\n         .sql(self.rhs))\n        if self._on is not None:\n            ctx.literal(' ON ').sql(self._on)\n        return ctx\n\n\nclass ValuesList(_HashableSource, BaseTable):\n    def __init__(self, values, columns=None, alias=None):\n        self._values = values\n        self._columns = columns\n        super(ValuesList, self).__init__(alias=alias)\n\n    def _get_hash(self):\n        return hash((self.__class__, id(self._values), self._alias))\n\n    @Node.copy\n    def columns(self, *names):\n        self._columns = names\n\n    def __sql__(self, ctx):\n        if self._alias:\n            ctx.alias_manager[self] = self._alias\n\n        if ctx.scope == SCOPE_SOURCE or ctx.scope == SCOPE_NORMAL:\n            with ctx(parentheses=not ctx.parentheses):\n                ctx = (ctx\n                       .literal('VALUES ')\n                       .sql(CommaNodeList([\n                           EnclosedNodeList(row) for row in self._values])))\n\n            if ctx.scope == SCOPE_SOURCE:\n                ctx.literal(' AS ').sql(Entity(ctx.alias_manager[self]))\n                if self._columns:\n                    entities = [Entity(c) for c in self._columns]\n                    ctx.sql(EnclosedNodeList(entities))\n        else:\n            ctx.sql(Entity(ctx.alias_manager[self]))\n\n        return ctx\n\n\nclass CTE(_HashableSource, Source):\n    def __init__(self, name, query, recursive=False, columns=None,\n                 materialized=None):\n        self._alias = name\n        self._query = query\n        self._recursive = recursive\n        self._materialized = materialized\n        if columns is not None:\n            columns = [Entity(c) if isinstance(c, str) else c\n                       for c in columns]\n        self._columns = columns\n        query._cte_list = ()\n        super(CTE, self).__init__(alias=name)\n\n    def select_from(self, *columns):\n        if not columns:\n            raise ValueError('select_from() must specify one or more columns '\n                             'from the CTE to select.')\n\n        query = (Select((self,), columns)\n                 .with_cte(self)\n                 .bind(self._query._database))\n        try:\n            query = query.objects(self._query.model)\n        except AttributeError:\n            pass\n        return query\n\n    def _get_hash(self):\n        return hash((self.__class__, self._alias, id(self._query)))\n\n    def union_all(self, rhs):\n        clone = self._query.clone()\n        return CTE(self._alias, clone + rhs, self._recursive, self._columns)\n    __add__ = union_all\n\n    def union(self, rhs):\n        clone = self._query.clone()\n        return CTE(self._alias, clone | rhs, self._recursive, self._columns)\n    __or__ = union\n\n    def __sql__(self, ctx):\n        if ctx.scope != SCOPE_CTE:\n            return ctx.sql(Entity(self._alias))\n\n        with ctx.push_alias():\n            ctx.alias_manager[self] = self._alias\n            ctx.sql(Entity(self._alias))\n\n            if self._columns:\n                ctx.literal(' ').sql(EnclosedNodeList(self._columns))\n            ctx.literal(' AS ')\n\n            if self._materialized:\n                ctx.literal('MATERIALIZED ')\n            elif self._materialized is False:\n                ctx.literal('NOT MATERIALIZED ')\n\n            with ctx.scope_normal(parentheses=True):\n                ctx.sql(self._query)\n        return ctx\n\n\nclass ColumnBase(Node):\n    _converter = None\n\n    @Node.copy\n    def converter(self, converter=None):\n        self._converter = converter\n\n    def alias(self, alias):\n        if alias:\n            return Alias(self, alias)\n        return self\n\n    def unalias(self):\n        return self\n\n    def bind_to(self, dest):\n        return BindTo(self, dest)\n\n    def cast(self, as_type):\n        return Cast(self, as_type)\n\n    def asc(self, collation=None, nulls=None):\n        return Asc(self, collation=collation, nulls=nulls)\n    __pos__ = asc\n\n    def desc(self, collation=None, nulls=None):\n        return Desc(self, collation=collation, nulls=nulls)\n    __neg__ = desc\n\n    def __invert__(self):\n        return Negated(self)\n\n    def _e(op, inv=False):\n        \"\"\"\n        Lightweight factory which returns a method that builds an Expression\n        consisting of the left-hand and right-hand operands, using `op`.\n        \"\"\"\n        def inner(self, rhs):\n            if inv:\n                return Expression(rhs, op, self)\n            return Expression(self, op, rhs)\n        return inner\n    __and__ = _e(OP.AND)\n    __or__ = _e(OP.OR)\n\n    __add__ = _e(OP.ADD)\n    __sub__ = _e(OP.SUB)\n    __mul__ = _e(OP.MUL)\n    __div__ = __truediv__ = _e(OP.DIV)\n    __xor__ = _e(OP.XOR)\n    __radd__ = _e(OP.ADD, inv=True)\n    __rsub__ = _e(OP.SUB, inv=True)\n    __rmul__ = _e(OP.MUL, inv=True)\n    __rdiv__ = __rtruediv__ = _e(OP.DIV, inv=True)\n    __rand__ = _e(OP.AND, inv=True)\n    __ror__ = _e(OP.OR, inv=True)\n    __rxor__ = _e(OP.XOR, inv=True)\n\n    def __eq__(self, rhs):\n        op = OP.IS if rhs is None else OP.EQ\n        return Expression(self, op, rhs)\n    def __ne__(self, rhs):\n        op = OP.IS_NOT if rhs is None else OP.NE\n        return Expression(self, op, rhs)\n\n    __lt__ = _e(OP.LT)\n    __le__ = _e(OP.LTE)\n    __gt__ = _e(OP.GT)\n    __ge__ = _e(OP.GTE)\n    __lshift__ = _e(OP.IN)\n    __rshift__ = _e(OP.IS)\n    __mod__ = _e(OP.LIKE)\n    __pow__ = _e(OP.ILIKE)\n\n    like = _e(OP.LIKE)\n    ilike = _e(OP.ILIKE)\n\n    bin_and = _e(OP.BIN_AND)\n    bin_or = _e(OP.BIN_OR)\n    in_ = _e(OP.IN)\n    not_in = _e(OP.NOT_IN)\n    regexp = _e(OP.REGEXP)\n    iregexp = _e(OP.IREGEXP)\n\n    # Special expressions.\n    def is_null(self, is_null=True):\n        op = OP.IS if is_null else OP.IS_NOT\n        return Expression(self, op, None)\n\n    def _escape_like_expr(self, s, template):\n        if s.find('_') >= 0 or s.find('%') >= 0 or s.find('\\\\') >= 0:\n            s = s.replace('\\\\', '\\\\\\\\').replace('_', '\\\\_').replace('%', '\\\\%')\n            # Pass the expression and escape string as unconverted values, to\n            # avoid (e.g.) a Json field converter turning the escaped LIKE\n            # pattern into a Json-quoted string.\n            return NodeList((\n                Value(template % s, converter=False),\n                SQL('ESCAPE'),\n                Value('\\\\', converter=False)))\n        return template % s\n    def contains(self, rhs):\n        if isinstance(rhs, Node):\n            rhs = Expression('%', OP.CONCAT,\n                             Expression(rhs, OP.CONCAT, '%'))\n        else:\n            rhs = self._escape_like_expr(rhs, '%%%s%%')\n        return Expression(self, OP.ILIKE, rhs)\n    def startswith(self, rhs):\n        if isinstance(rhs, Node):\n            rhs = Expression(rhs, OP.CONCAT, '%')\n        else:\n            rhs = self._escape_like_expr(rhs, '%s%%')\n        return Expression(self, OP.ILIKE, rhs)\n    def endswith(self, rhs):\n        if isinstance(rhs, Node):\n            rhs = Expression('%', OP.CONCAT, rhs)\n        else:\n            rhs = self._escape_like_expr(rhs, '%%%s')\n        return Expression(self, OP.ILIKE, rhs)\n    def between(self, lo, hi):\n        return Expression(self, OP.BETWEEN, NodeList((lo, SQL('AND'), hi)))\n    def concat(self, rhs):\n        return StringExpression(self, OP.CONCAT, rhs)\n    def __getitem__(self, item):\n        if isinstance(item, slice):\n            if item.start is None or item.stop is None:\n                raise ValueError('BETWEEN range must have both a start- and '\n                                 'end-point.')\n            return self.between(item.start, item.stop)\n        return self == item\n    __iter__ = None  # Prevent infinite loop.\n\n    def distinct(self):\n        return NodeList((SQL('DISTINCT'), self))\n\n    def collate(self, collation):\n        return NodeList((self, SQL('COLLATE %s' % collation)))\n\n    def get_sort_key(self, ctx):\n        return ()\n\n\nclass Column(ColumnBase):\n    def __init__(self, source, name):\n        self.source = source\n        self.name = name\n\n    def get_sort_key(self, ctx):\n        if ctx.scope == SCOPE_VALUES:\n            return (self.name,)\n        else:\n            return self.source.get_sort_key(ctx) + (self.name,)\n\n    def __hash__(self):\n        return hash((self.source, self.name))\n\n    def __sql__(self, ctx):\n        if ctx.scope == SCOPE_VALUES:\n            return ctx.sql(Entity(self.name))\n        else:\n            with ctx.scope_column():\n                return ctx.sql(self.source).literal('.').sql(Entity(self.name))\n\n\nclass WrappedNode(ColumnBase):\n    def __init__(self, node):\n        self.node = node\n        self._coerce = getattr(node, '_coerce', True)\n        self._converter = getattr(node, '_converter', None)\n\n    def is_alias(self):\n        return self.node.is_alias()\n\n    def unwrap(self):\n        return self.node.unwrap()\n\n\nclass EntityFactory(object):\n    __slots__ = ('node',)\n    def __init__(self, node):\n        self.node = node\n    def __getattr__(self, attr):\n        return Entity(self.node, attr)\n\n\nclass _DynamicEntity(object):\n    __slots__ = ()\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            return EntityFactory(instance._alias)  # Implements __getattr__().\n        return self\n\n\nclass Alias(WrappedNode):\n    c = _DynamicEntity()\n\n    def __init__(self, node, alias):\n        super(Alias, self).__init__(node)\n        self._alias = alias\n\n    def __hash__(self):\n        return hash(self._alias)\n\n    @property\n    def name(self):\n        return self._alias\n    @name.setter\n    def name(self, value):\n        self._alias = value\n\n    def alias(self, alias=None):\n        if alias is None:\n            return self.node\n        else:\n            return Alias(self.node, alias)\n\n    def unalias(self):\n        return self.node\n\n    def is_alias(self):\n        return True\n\n    def __sql__(self, ctx):\n        if ctx.scope == SCOPE_SOURCE:\n            return (ctx\n                    .sql(self.node)\n                    .literal(' AS ')\n                    .sql(Entity(self._alias)))\n        else:\n            return ctx.sql(Entity(self._alias))\n\n\nclass BindTo(WrappedNode):\n    def __init__(self, node, dest):\n        super(BindTo, self).__init__(node)\n        self.dest = dest\n\n    def __sql__(self, ctx):\n        return ctx.sql(self.node)\n\n\nclass Negated(WrappedNode):\n    def __invert__(self):\n        return self.node\n\n    def __sql__(self, ctx):\n        return ctx.literal('NOT ').sql(self.node)\n\n\nclass BitwiseMixin(object):\n    def __and__(self, other):\n        return self.bin_and(other)\n\n    def __or__(self, other):\n        return self.bin_or(other)\n\n    def __sub__(self, other):\n        return self.bin_and(other.bin_negated())\n\n    def __invert__(self):\n        return BitwiseNegated(self)\n\n\nclass BitwiseNegated(BitwiseMixin, WrappedNode):\n    op = OP.BITWISE_NEGATION\n\n    def __invert__(self):\n        return self.node\n\n    def __sql__(self, ctx):\n        if ctx.state.operations:\n            op_sql = ctx.state.operations.get(self.op, self.op)\n        else:\n            op_sql = self.op\n        return ctx.literal(op_sql).sql(self.node)\n\n\nclass Value(ColumnBase):\n    def __init__(self, value, converter=None, unpack=True):\n        self.value = value\n        self.converter = converter\n        self.multi = unpack and isinstance(self.value, multi_types)\n        if self.multi:\n            self.values = []\n            for item in self.value:\n                if isinstance(item, Node):\n                    self.values.append(item)\n                else:\n                    self.values.append(Value(item, self.converter))\n        else:\n            self.values = None\n\n    def __sql__(self, ctx):\n        if self.multi:\n            # For multi-part values (e.g. lists of IDs).\n            return ctx.sql(EnclosedNodeList(self.values))\n\n        return ctx.value(self.value, self.converter)\n\n\nclass ValueLiterals(WrappedNode):\n    def __sql__(self, ctx):\n        with ctx(value_literals=True):\n            return ctx.sql(self.node)\n\n\ndef AsIs(value, converter=None):\n    return Value(value, converter, unpack=False)\n\n\nclass Cast(WrappedNode):\n    def __init__(self, node, cast):\n        super(Cast, self).__init__(node)\n        self._cast = cast\n        self._coerce = False\n\n    def __sql__(self, ctx):\n        return (ctx\n                .literal('CAST(')\n                .sql(self.node)\n                .literal(' AS %s)' % self._cast))\n\n\nclass Ordering(WrappedNode):\n    def __init__(self, node, direction, collation=None, nulls=None):\n        super(Ordering, self).__init__(node)\n        self.direction = direction\n        self.collation = collation\n        self.nulls = nulls\n        if nulls and nulls.lower() not in ('first', 'last'):\n            raise ValueError('Ordering nulls= parameter must be \"first\" or '\n                             '\"last\", got: %s' % nulls)\n\n    def collate(self, collation=None):\n        return Ordering(self.node, self.direction, collation)\n\n    def _null_ordering_case(self, nulls):\n        if nulls.lower() == 'last':\n            ifnull, notnull = 1, 0\n        elif nulls.lower() == 'first':\n            ifnull, notnull = 0, 1\n        else:\n            raise ValueError('unsupported value for nulls= ordering.')\n        return Case(None, ((self.node.is_null(), ifnull),), notnull)\n\n    def __sql__(self, ctx):\n        if self.nulls and not ctx.state.nulls_ordering:\n            ctx.sql(self._null_ordering_case(self.nulls)).literal(', ')\n\n        ctx.sql(self.node).literal(' %s' % self.direction)\n        if self.collation:\n            ctx.literal(' COLLATE %s' % self.collation)\n        if self.nulls and ctx.state.nulls_ordering:\n            ctx.literal(' NULLS %s' % self.nulls)\n        return ctx\n\n\ndef Asc(node, collation=None, nulls=None):\n    return Ordering(node, 'ASC', collation, nulls)\n\n\ndef Desc(node, collation=None, nulls=None):\n    return Ordering(node, 'DESC', collation, nulls)\n\n\nclass Expression(ColumnBase):\n    def __init__(self, lhs, op, rhs, flat=False):\n        self.lhs = lhs\n        self.op = op\n        self.rhs = rhs\n        self.flat = flat\n\n    def __sql__(self, ctx):\n        overrides = {'parentheses': not self.flat, 'in_expr': True}\n\n        # First attempt to unwrap the node on the left-hand-side, so that we\n        # can get at the underlying Field if one is present.\n        node = raw_node = self.lhs\n        if isinstance(raw_node, WrappedNode):\n            node = raw_node.unwrap()\n\n        # Set up the appropriate converter if we have a field on the left side.\n        if isinstance(node, Field) and raw_node._coerce:\n            overrides['converter'] = node.db_value\n            overrides['is_fk_expr'] = isinstance(node, ForeignKeyField)\n        else:\n            overrides['converter'] = None\n\n        if ctx.state.operations:\n            op_sql = ctx.state.operations.get(self.op, self.op)\n        else:\n            op_sql = self.op\n\n        with ctx(**overrides):\n            # Postgresql reports an error for IN/NOT IN (), so convert to\n            # the equivalent boolean expression.\n            op_in = self.op == OP.IN or self.op == OP.NOT_IN\n            rhs = self.rhs\n            if op_in:\n                #\n                if self._is_rhs_empty(rhs, ctx):\n                    return ctx.literal('0 = 1' if self.op == OP.IN else '1 = 1')\n            if rhs is None and (self.op == OP.IS or self.op == OP.IS_NOT):\n                rhs = SQL('NULL')\n\n            return (ctx\n                    .sql(self.lhs)\n                    .literal(' %s ' % op_sql)\n                    .sql(rhs))\n\n    def _is_rhs_empty(self, rhs, ctx):\n        if isinstance(rhs, multi_types):\n            return not bool(rhs)\n        elif isinstance(rhs, Value):\n            return (rhs.multi and not rhs.values)\n        else:\n            return ctx.as_new().parse(rhs)[0] == '()'\n\n\nclass StringExpression(Expression):\n    def __add__(self, rhs):\n        return self.concat(rhs)\n    def __radd__(self, lhs):\n        return StringExpression(lhs, OP.CONCAT, self)\n\n\nclass Entity(ColumnBase):\n    def __init__(self, *path):\n        self._path = [p for p in path if p]\n\n    def __getattr__(self, attr):\n        return Entity(*self._path + [attr])\n\n    def get_sort_key(self, ctx):\n        return tuple(self._path)\n\n    def __hash__(self):\n        return hash((self.__class__.__name__, tuple(self._path)))\n\n    def __sql__(self, ctx):\n        quote_chars = ctx.state.quote or '\"\"'\n        q = quote_chars[0]\n        escaped = [p.replace(q, quote_chars) for p in self._path]\n        return ctx.literal(quote(escaped, quote_chars))\n\n\nclass SQL(ColumnBase):\n    def __init__(self, sql, params=None):\n        self.sql = sql\n        self.params = params\n\n    def __sql__(self, ctx):\n        ctx.literal(self.sql)\n        if self.params:\n            for param in self.params:\n                ctx.value(param, False, add_param=False)\n        return ctx\n\n\ndef Check(constraint, name=None):\n    check = SQL('CHECK (%s)' % constraint)\n    if not name:\n        return check\n    return NodeList((SQL('CONSTRAINT'), Entity(name), check))\n\n\ndef Default(value):\n    return SQL('DEFAULT %s' % value)\n\n\nclass Function(ColumnBase):\n    no_coerce_functions = set(('sum', 'count', 'avg', 'cast', 'array_agg'))\n\n    def __init__(self, name, arguments, coerce=True, python_value=None):\n        self.name = name\n        self.arguments = arguments\n        self._filter = None\n        self._order_by = None\n        self._python_value = python_value\n        if name and name.lower() in self.no_coerce_functions:\n            self._coerce = False\n        else:\n            self._coerce = coerce\n\n    def __getattr__(self, attr):\n        def decorator(*args, **kwargs):\n            return Function(attr, args, **kwargs)\n        return decorator\n\n    @Node.copy\n    def filter(self, where=None):\n        self._filter = where\n\n    @Node.copy\n    def order_by(self, *ordering):\n        self._order_by = ordering\n\n    @Node.copy\n    def python_value(self, func=None):\n        self._python_value = func\n\n    def over(self, partition_by=None, order_by=None, start=None, end=None,\n             frame_type=None, window=None, exclude=None):\n        if isinstance(partition_by, Window) and window is None:\n            window = partition_by\n\n        if window is not None:\n            node = WindowAlias(window)\n        else:\n            node = Window(partition_by=partition_by, order_by=order_by,\n                          start=start, end=end, frame_type=frame_type,\n                          exclude=exclude, _inline=True)\n        return NodeList((self, SQL('OVER'), node))\n\n    def __sql__(self, ctx):\n        ctx.literal(self.name)\n        if not len(self.arguments):\n            ctx.literal('()')\n        else:\n            args = self.arguments\n\n            # If this is an ordered aggregate, then we will modify the last\n            # argument to append the ORDER BY ... clause. We do this to avoid\n            # double-wrapping any expression args in parentheses, as NodeList\n            # has a special check (hack) in place to work around this.\n            if self._order_by:\n                args = list(args)\n                args[-1] = NodeList((args[-1], SQL('ORDER BY'),\n                                     CommaNodeList(self._order_by)))\n\n            with ctx(in_function=True, function_arg_count=len(self.arguments)):\n                ctx.sql(EnclosedNodeList([\n                    (arg if isinstance(arg, Node) else Value(arg, False))\n                    for arg in args]))\n\n        if self._filter:\n            ctx.literal(' FILTER (WHERE ').sql(self._filter).literal(')')\n        return ctx\n\n\nfn = Function(None, None)\n\n\nclass Window(Node):\n    # Frame start/end and frame exclusion.\n    CURRENT_ROW = SQL('CURRENT ROW')\n    GROUP = SQL('GROUP')\n    TIES = SQL('TIES')\n    NO_OTHERS = SQL('NO OTHERS')\n\n    # Frame types.\n    GROUPS = 'GROUPS'\n    RANGE = 'RANGE'\n    ROWS = 'ROWS'\n\n    def __init__(self, partition_by=None, order_by=None, start=None, end=None,\n                 frame_type=None, extends=None, exclude=None, alias=None,\n                 _inline=False):\n        super(Window, self).__init__()\n        if start is not None and not isinstance(start, SQL):\n            start = SQL(start)\n        if end is not None and not isinstance(end, SQL):\n            end = SQL(end)\n\n        self.partition_by = ensure_tuple(partition_by)\n        self.order_by = ensure_tuple(order_by)\n        self.start = start\n        self.end = end\n        if self.start is None and self.end is not None:\n            raise ValueError('Cannot specify WINDOW end without start.')\n        self._alias = alias or 'w'\n        self._inline = _inline\n        self.frame_type = frame_type\n        self._extends = extends\n        self._exclude = exclude\n\n    def alias(self, alias=None):\n        self._alias = alias or 'w'\n        return self\n\n    @Node.copy\n    def as_range(self):\n        self.frame_type = Window.RANGE\n\n    @Node.copy\n    def as_rows(self):\n        self.frame_type = Window.ROWS\n\n    @Node.copy\n    def as_groups(self):\n        self.frame_type = Window.GROUPS\n\n    @Node.copy\n    def extends(self, window=None):\n        self._extends = window\n\n    @Node.copy\n    def exclude(self, frame_exclusion=None):\n        if isinstance(frame_exclusion, str):\n            frame_exclusion = SQL(frame_exclusion)\n        self._exclude = frame_exclusion\n\n    @staticmethod\n    def following(value=None):\n        if value is None:\n            return SQL('UNBOUNDED FOLLOWING')\n        return SQL('%d FOLLOWING' % value)\n\n    @staticmethod\n    def preceding(value=None):\n        if value is None:\n            return SQL('UNBOUNDED PRECEDING')\n        return SQL('%d PRECEDING' % value)\n\n    def __sql__(self, ctx):\n        if ctx.scope != SCOPE_SOURCE and not self._inline:\n            ctx.sql(Entity(self._alias))\n            ctx.literal(' AS ')\n\n        with ctx(parentheses=True):\n            parts = []\n            if self._extends is not None:\n                ext = self._extends\n                if isinstance(ext, Window):\n                    ext = Entity(ext._alias)\n                elif isinstance(ext, str):\n                    ext = Entity(ext)\n                parts.append(ext)\n            if self.partition_by:\n                parts.extend((\n                    SQL('PARTITION BY'),\n                    CommaNodeList(self.partition_by)))\n            if self.order_by:\n                parts.extend((\n                    SQL('ORDER BY'),\n                    CommaNodeList(self.order_by)))\n            if self.start is not None and self.end is not None:\n                frame = self.frame_type or 'ROWS'\n                parts.extend((\n                    SQL('%s BETWEEN' % frame),\n                    self.start,\n                    SQL('AND'),\n                    self.end))\n            elif self.start is not None:\n                parts.extend((SQL(self.frame_type or 'ROWS'), self.start))\n            elif self.frame_type is not None:\n                parts.append(SQL('%s UNBOUNDED PRECEDING' % self.frame_type))\n            if self._exclude is not None:\n                parts.extend((SQL('EXCLUDE'), self._exclude))\n            ctx.sql(NodeList(parts))\n        return ctx\n\n\nclass WindowAlias(Node):\n    def __init__(self, window):\n        self.window = window\n\n    def alias(self, window_alias):\n        self.window._alias = window_alias\n        return self\n\n    def __sql__(self, ctx):\n        return ctx.sql(Entity(self.window._alias or 'w'))\n\n\nclass _InFunction(Node):\n    def __init__(self, node, in_function=True):\n        self.node = node\n        self.in_function = in_function\n\n    def __sql__(self, ctx):\n        with ctx(in_function=self.in_function):\n            return ctx.sql(self.node)\n\n\nclass Case(ColumnBase):\n    def __init__(self, predicate, expression_tuples, default=None):\n        self.predicate = predicate\n        self.expression_tuples = expression_tuples\n        self.default = default\n\n    def __sql__(self, ctx):\n        clauses = [SQL('CASE')]\n        if self.predicate is not None:\n            clauses.append(self.predicate)\n        for expr, value in self.expression_tuples:\n            clauses.extend((SQL('WHEN'), expr,\n                            SQL('THEN'), _InFunction(value)))\n        if self.default is not None:\n            clauses.extend((SQL('ELSE'), _InFunction(self.default)))\n        clauses.append(SQL('END'))\n        with ctx(in_function=False):\n            return ctx.sql(NodeList(clauses))\n\n\nclass ForUpdate(Node):\n    def __init__(self, expr, of=None, nowait=None, skip_locked=None):\n        expr = 'FOR UPDATE' if expr is True else expr\n        if expr.lower().endswith('nowait'):\n            expr = expr[:-7]  # Strip off the \"nowait\" bit.\n            nowait = True\n        elif expr.lower().endswith('skip locked'):\n            expr = expr[:-12]\n            skip_locked = True\n\n        if nowait and skip_locked:\n            raise ValueError('Only one of nowait and skip_locked may be used '\n                             'in a FOR UPDATE clause.')\n\n        self._expr = expr\n        if of is not None and not isinstance(of, (list, set, tuple)):\n            of = (of,)\n        self._of = of\n        self._nowait = nowait\n        self._skip_locked = skip_locked\n\n    def __sql__(self, ctx):\n        ctx.literal(self._expr)\n        if self._of is not None:\n            ctx.literal(' OF ').sql(CommaNodeList(self._of))\n        if self._nowait:\n            ctx.literal(' NOWAIT')\n        elif self._skip_locked:\n            ctx.literal(' SKIP LOCKED')\n        return ctx\n\n\nclass NodeList(ColumnBase):\n    def __init__(self, nodes, glue=' ', parens=False):\n        self.nodes = nodes\n        self.glue = glue\n        self.parens = parens\n\n    def __sql__(self, ctx):\n        n_nodes = len(self.nodes)\n        if n_nodes == 0:\n            return ctx.literal('()') if self.parens else ctx\n        elif self.parens and n_nodes == 1 and \\\n           isinstance(self.nodes[0], Expression) and \\\n           not self.nodes[0].flat:\n            # Hack to avoid double-parentheses.\n            nodes = (self.nodes[0].clone(),)\n            nodes[0].flat = True\n        else:\n            nodes = self.nodes\n\n        with ctx(parentheses=self.parens):\n            for i in range(n_nodes - 1):\n                ctx.sql(nodes[i])\n                ctx.literal(self.glue)\n            ctx.sql(nodes[n_nodes - 1])\n        return ctx\n\n\ndef CommaNodeList(nodes):\n    return NodeList(nodes, ', ')\n\n\ndef EnclosedNodeList(nodes):\n    return NodeList(nodes, ', ', True)\n\n\nclass _Namespace(Node):\n    __slots__ = ('_name',)\n    def __init__(self, name):\n        self._name = name\n    def __getattr__(self, attr):\n        return NamespaceAttribute(self, attr)\n    __getitem__ = __getattr__\n\nclass NamespaceAttribute(ColumnBase):\n    def __init__(self, namespace, attribute):\n        self._namespace = namespace\n        self._attribute = attribute\n\n    def __sql__(self, ctx):\n        return (ctx\n                .literal(self._namespace._name + '.')\n                .sql(Entity(self._attribute)))\n\nEXCLUDED = _Namespace('EXCLUDED')\n\n\nclass DQ(ColumnBase):\n    def __init__(self, **query):\n        super(DQ, self).__init__()\n        self.query = query\n        self._negated = False\n\n    @Node.copy\n    def __invert__(self):\n        self._negated = not self._negated\n\n    def clone(self):\n        node = DQ(**self.query)\n        node._negated = self._negated\n        return node\n\n#: Represent a row tuple.\nTuple = lambda *a: EnclosedNodeList(a)\n\n\nclass QualifiedNames(WrappedNode):\n    def __sql__(self, ctx):\n        with ctx.scope_column():\n            return ctx.sql(self.node)\n\n\ndef qualify_names(node):\n    # Search a node heirarchy to ensure that any column-like objects are\n    # referenced using fully-qualified names.\n    if isinstance(node, Expression):\n        return node.__class__(qualify_names(node.lhs), node.op,\n                              qualify_names(node.rhs), node.flat)\n    elif isinstance(node, ColumnBase):\n        return QualifiedNames(node)\n    return node\n\n\nclass OnConflict(Node):\n    def __init__(self, action=None, update=None, preserve=None, where=None,\n                 conflict_target=None, conflict_where=None,\n                 conflict_constraint=None):\n        self._action = action\n        self._update = update\n        self._preserve = ensure_tuple(preserve)\n        self._where = where\n        if conflict_target is not None and conflict_constraint is not None:\n            raise ValueError('only one of \"conflict_target\" and '\n                             '\"conflict_constraint\" may be specified.')\n        self._conflict_target = ensure_tuple(conflict_target)\n        self._conflict_where = conflict_where\n        self._conflict_constraint = conflict_constraint\n\n    def get_conflict_statement(self, ctx, query):\n        return ctx.state.conflict_statement(self, query)\n\n    def get_conflict_update(self, ctx, query):\n        return ctx.state.conflict_update(self, query)\n\n    @Node.copy\n    def preserve(self, *columns):\n        self._preserve = columns\n\n    @Node.copy\n    def update(self, _data=None, **kwargs):\n        if _data and kwargs and not isinstance(_data, dict):\n            raise ValueError('Cannot mix data with keyword arguments in the '\n                             'OnConflict update method.')\n        _data = _data or {}\n        if kwargs:\n            _data.update(kwargs)\n        self._update = _data\n\n    @Node.copy\n    def where(self, *expressions):\n        if self._where is not None:\n            expressions = (self._where,) + expressions\n        self._where = reduce(operator.and_, expressions)\n\n    @Node.copy\n    def conflict_target(self, *constraints):\n        self._conflict_constraint = None\n        self._conflict_target = constraints\n\n    @Node.copy\n    def conflict_where(self, *expressions):\n        if self._conflict_where is not None:\n            expressions = (self._conflict_where,) + expressions\n        self._conflict_where = reduce(operator.and_, expressions)\n\n    @Node.copy\n    def conflict_constraint(self, constraint):\n        self._conflict_constraint = constraint\n        self._conflict_target = None\n\n\ndef database_required(method):\n    @wraps(method)\n    def inner(self, database=None, *args, **kwargs):\n        database = self._database if database is None else database\n        if not database:\n            raise InterfaceError('Query must be bound to a database in order '\n                                 'to call \"%s\".' % method.__name__)\n        return method(self, database, *args, **kwargs)\n    return inner\n\n# BASE QUERY INTERFACE.\n\nclass BaseQuery(Node):\n    default_row_type = ROW.DICT\n\n    def __init__(self, _database=None, **kwargs):\n        self._database = _database\n        self._cursor_wrapper = None\n        self._row_type = None\n        self._constructor = None\n        super(BaseQuery, self).__init__(**kwargs)\n\n    def bind(self, database=None):\n        self._database = database\n        return self\n\n    def clone(self):\n        query = super(BaseQuery, self).clone()\n        query._cursor_wrapper = None\n        return query\n\n    @Node.copy\n    def dicts(self, as_dict=True):\n        self._row_type = ROW.DICT if as_dict else None\n        return self\n\n    @Node.copy\n    def tuples(self, as_tuple=True):\n        self._row_type = ROW.TUPLE if as_tuple else None\n        return self\n\n    @Node.copy\n    def namedtuples(self, as_namedtuple=True):\n        self._row_type = ROW.NAMED_TUPLE if as_namedtuple else None\n        return self\n\n    @Node.copy\n    def objects(self, constructor=None):\n        self._row_type = ROW.CONSTRUCTOR if constructor else None\n        self._constructor = constructor\n        return self\n\n    def _get_cursor_wrapper(self, cursor):\n        row_type = self._row_type or self.default_row_type\n\n        if row_type == ROW.DICT:\n            return DictCursorWrapper(cursor)\n        elif row_type == ROW.TUPLE:\n            return CursorWrapper(cursor)\n        elif row_type == ROW.NAMED_TUPLE:\n            return NamedTupleCursorWrapper(cursor)\n        elif row_type == ROW.CONSTRUCTOR:\n            return ObjectCursorWrapper(cursor, self._constructor)\n        else:\n            raise ValueError('Unrecognized row type: \"%s\".' % row_type)\n\n    def __sql__(self, ctx):\n        raise NotImplementedError\n\n    def sql(self):\n        if self._database:\n            context = self._database.get_sql_context()\n        else:\n            context = Context()\n        return context.parse(self)\n\n    @database_required\n    def execute(self, database):\n        return self._execute(database)\n\n    def _execute(self, database):\n        raise NotImplementedError\n\n    def iterator(self, database=None):\n        return iter(self.execute(database).iterator())\n\n    def _ensure_execution(self):\n        if self._cursor_wrapper is None:\n            if not self._database:\n                raise ValueError('Query has not been executed.')\n            self.execute()\n\n    def __iter__(self):\n        self._ensure_execution()\n        return iter(self._cursor_wrapper)\n\n    def __getitem__(self, value):\n        self._ensure_execution()\n        if isinstance(value, slice):\n            index = value.stop\n        else:\n            index = value\n        if index is not None:\n            index = index + 1 if index >= 0 else 0\n        self._cursor_wrapper.fill_cache(index)\n        return self._cursor_wrapper.row_cache[value]\n\n    def __len__(self):\n        self._ensure_execution()\n        return len(self._cursor_wrapper)\n\n    def __str__(self):\n        return query_to_string(self)\n\n\nclass RawQuery(BaseQuery):\n    def __init__(self, sql=None, params=None, **kwargs):\n        super(RawQuery, self).__init__(**kwargs)\n        self._sql = sql\n        self._params = params\n\n    def __sql__(self, ctx):\n        ctx.literal(self._sql)\n        if self._params:\n            for param in self._params:\n                ctx.value(param, add_param=False)\n        return ctx\n\n    def _execute(self, database):\n        if self._cursor_wrapper is None:\n            cursor = database.execute(self)\n            self._cursor_wrapper = self._get_cursor_wrapper(cursor)\n        return self._cursor_wrapper\n\n\nclass Query(BaseQuery):\n    def __init__(self, where=None, order_by=None, limit=None, offset=None,\n                 **kwargs):\n        super(Query, self).__init__(**kwargs)\n        self._where = where\n        self._order_by = order_by\n        self._limit = limit\n        self._offset = offset\n\n        self._cte_list = None\n\n    @Node.copy\n    def with_cte(self, *cte_list):\n        self._cte_list = cte_list\n\n    @Node.copy\n    def where(self, *expressions):\n        if self._where is not None:\n            expressions = (self._where,) + expressions\n        self._where = reduce(operator.and_, expressions)\n\n    @Node.copy\n    def orwhere(self, *expressions):\n        if self._where is not None:\n            expressions = (self._where,) + expressions\n        self._where = reduce(operator.or_, expressions)\n\n    @Node.copy\n    def order_by(self, *values):\n        self._order_by = values\n\n    @Node.copy\n    def order_by_extend(self, *values):\n        self._order_by = ((self._order_by or ()) + values) or None\n\n    @Node.copy\n    def limit(self, value=None):\n        self._limit = value\n\n    @Node.copy\n    def offset(self, value=None):\n        self._offset = value\n\n    @Node.copy\n    def paginate(self, page, paginate_by=20):\n        page = page - 1 if page > 0 else 0\n        self._limit = paginate_by\n        self._offset = page * paginate_by\n\n    def _apply_ordering(self, ctx):\n        if self._order_by:\n            (ctx\n             .literal(' ORDER BY ')\n             .sql(CommaNodeList(self._order_by)))\n        if self._limit is not None or (self._offset is not None and\n                                       ctx.state.limit_max):\n            limit = ctx.state.limit_max if self._limit is None else self._limit\n            ctx.literal(' LIMIT ').sql(limit)\n        if self._offset is not None:\n            ctx.literal(' OFFSET ').sql(self._offset)\n        return ctx\n\n    def __sql__(self, ctx):\n        if self._cte_list:\n            # The CTE scope is only used at the very beginning of the query,\n            # when we are describing the various CTEs we will be using.\n            recursive = any(cte._recursive for cte in self._cte_list)\n\n            # Explicitly disable the \"subquery\" flag here, so as to avoid\n            # unnecessary parentheses around subsequent selects.\n            with ctx.scope_cte(subquery=False):\n                (ctx\n                 .literal('WITH RECURSIVE ' if recursive else 'WITH ')\n                 .sql(CommaNodeList(self._cte_list))\n                 .literal(' '))\n        return ctx\n\n\ndef __compound_select__(operation, inverted=False):\n    @__bind_database__\n    def method(self, other):\n        if inverted:\n            self, other = other, self\n        return CompoundSelectQuery(self, operation, other)\n    return method\n\n\nclass SelectQuery(Query):\n    union_all = __add__ = __compound_select__('UNION ALL')\n    union = __or__ = __compound_select__('UNION')\n    intersect = __and__ = __compound_select__('INTERSECT')\n    except_ = __sub__ = __compound_select__('EXCEPT')\n    __radd__ = __compound_select__('UNION ALL', inverted=True)\n    __ror__ = __compound_select__('UNION', inverted=True)\n    __rand__ = __compound_select__('INTERSECT', inverted=True)\n    __rsub__ = __compound_select__('EXCEPT', inverted=True)\n\n    def select_from(self, *columns):\n        if not columns:\n            raise ValueError('select_from() must specify one or more columns.')\n\n        query = (Select((self,), columns)\n                 .bind(self._database))\n        if getattr(self, 'model', None) is not None:\n            # Bind to the sub-select's model type, if defined.\n            query = query.objects(self.model)\n        return query\n\n\nclass SelectBase(_HashableSource, Source, SelectQuery):\n    def _get_hash(self):\n        return hash((self.__class__, self._alias or id(self)))\n\n    def _execute(self, database):\n        if self._cursor_wrapper is None:\n            cursor = database.execute(self)\n            self._cursor_wrapper = self._get_cursor_wrapper(cursor)\n        return self._cursor_wrapper\n\n    @database_required\n    def peek(self, database, n=1):\n        rows = self.execute(database)[:n]\n        if rows:\n            return rows[0] if n == 1 else rows\n\n    @database_required\n    def first(self, database, n=1):\n        if self._limit != n:\n            self._limit = n\n            self._cursor_wrapper = None\n        return self.peek(database, n=n)\n\n    @database_required\n    def scalar(self, database, as_tuple=False, as_dict=False):\n        if as_dict:\n            return self.dicts().peek(database)\n        row = self.tuples().peek(database)\n        return row[0] if row and not as_tuple else row\n\n    @database_required\n    def scalars(self, database):\n        for row in self.tuples().execute(database):\n            yield row[0]\n\n    @database_required\n    def count(self, database, clear_limit=False):\n        clone = self.order_by().alias('_wrapped')\n        if clear_limit:\n            clone._limit = clone._offset = None\n        try:\n            if clone._having is None and clone._group_by is None and \\\n               clone._windows is None and clone._distinct is None and \\\n               clone._simple_distinct is not True:\n                clone = clone.select(SQL('1'))\n        except AttributeError:\n            pass\n        return Select([clone], [fn.COUNT(SQL('1'))]).scalar(database)\n\n    @database_required\n    def exists(self, database):\n        clone = self.columns(SQL('1'))\n        clone._limit = 1\n        clone._offset = None\n        return bool(clone.scalar())\n\n    @database_required\n    def get(self, database):\n        self._cursor_wrapper = None\n        try:\n            return self.execute(database)[0]\n        except IndexError:\n            pass\n\n\n# QUERY IMPLEMENTATIONS.\n\n\nclass CompoundSelectQuery(SelectBase):\n    def __init__(self, lhs, op, rhs):\n        super(CompoundSelectQuery, self).__init__()\n        self.lhs = lhs\n        self.op = op\n        self.rhs = rhs\n\n    @property\n    def _returning(self):\n        return self.lhs._returning\n\n    @database_required\n    def exists(self, database):\n        query = Select((self.limit(1),), (SQL('1'),)).bind(database)\n        return bool(query.scalar())\n\n    def _get_query_key(self):\n        return (self.lhs.get_query_key(), self.rhs.get_query_key())\n\n    def _wrap_parens(self, ctx, subq):\n        csq_setting = ctx.state.compound_select_parentheses\n\n        if not csq_setting or csq_setting == CSQ_PARENTHESES_NEVER:\n            return False\n        elif csq_setting == CSQ_PARENTHESES_ALWAYS:\n            return True\n        elif csq_setting == CSQ_PARENTHESES_UNNESTED:\n            if ctx.state.in_expr or ctx.state.in_function:\n                # If this compound select query is being used inside an\n                # expression, e.g., an IN or EXISTS().\n                return False\n\n            # If the query on the left or right is itself a compound select\n            # query, then we do not apply parentheses. However, if it is a\n            # regular SELECT query, we will apply parentheses.\n            return not isinstance(subq, CompoundSelectQuery)\n\n    def __sql__(self, ctx):\n        if ctx.scope == SCOPE_COLUMN:\n            return self.apply_column(ctx)\n\n        # Call parent method to handle any CTEs.\n        super(CompoundSelectQuery, self).__sql__(ctx)\n\n        outer_parens = ctx.subquery or (ctx.scope == SCOPE_SOURCE)\n        with ctx(parentheses=outer_parens):\n            # Should the left-hand query be wrapped in parentheses?\n            lhs_parens = self._wrap_parens(ctx, self.lhs)\n            with ctx.scope_normal(parentheses=lhs_parens, subquery=False):\n                ctx.sql(self.lhs)\n            ctx.literal(' %s ' % self.op)\n            with ctx.push_alias():\n                # Should the right-hand query be wrapped in parentheses?\n                rhs_parens = self._wrap_parens(ctx, self.rhs)\n                with ctx.scope_normal(parentheses=rhs_parens, subquery=False):\n                    ctx.sql(self.rhs)\n\n            # Apply ORDER BY, LIMIT, OFFSET. We use the \"values\" scope so that\n            # entity names are not fully-qualified. This is a bit of a hack, as\n            # we're relying on the logic in Column.__sql__() to not fully\n            # qualify column names.\n            with ctx.scope_values():\n                self._apply_ordering(ctx)\n\n        return self.apply_alias(ctx)\n\n\nclass Select(SelectBase):\n    def __init__(self, from_list=None, columns=None, group_by=None,\n                 having=None, distinct=None, windows=None, for_update=None,\n                 lateral=None, **kwargs):\n        super(Select, self).__init__(**kwargs)\n        self._from_list = (list(from_list) if isinstance(from_list, tuple)\n                           else from_list) or []\n        self._returning = columns\n        self._group_by = group_by\n        self._having = having\n        self._windows = None\n        self._for_update = for_update\n        self._lateral = lateral\n\n        self._distinct = self._simple_distinct = None\n        if distinct:\n            if isinstance(distinct, bool):\n                self._simple_distinct = distinct\n            else:\n                self._distinct = distinct\n\n        self._cursor_wrapper = None\n\n    def clone(self):\n        clone = super(Select, self).clone()\n        if clone._from_list:\n            clone._from_list = list(clone._from_list)\n        return clone\n\n    @Node.copy\n    def columns(self, *columns, **kwargs):\n        self._returning = columns\n    select = columns\n\n    @Node.copy\n    def select_extend(self, *columns):\n        self._returning = tuple(self._returning) + columns\n\n    @property\n    def selected_columns(self):\n        return self._returning\n    @selected_columns.setter\n    def selected_columns(self, value):\n        self._returning = value\n\n    @Node.copy\n    def from_(self, *sources):\n        self._from_list = list(sources)\n\n    @Node.copy\n    def join(self, dest, join_type=JOIN.INNER, on=None):\n        if not self._from_list:\n            raise ValueError('No sources to join on.')\n        item = self._from_list.pop()\n        if join_type == JOIN.LATERAL or join_type == JOIN.LEFT_LATERAL:\n            on = True\n        self._from_list.append(Join(item, dest, join_type, on))\n\n    def left_outer_join(self, dest, on=None):\n        return self.join(dest, JOIN.LEFT_OUTER, on)\n\n    @Node.copy\n    def group_by(self, *columns):\n        grouping = []\n        for column in columns:\n            if isinstance(column, Table):\n                if not column._columns:\n                    raise ValueError('Cannot pass a table to group_by() that '\n                                     'does not have columns explicitly '\n                                     'declared.')\n                grouping.extend([getattr(column, col_name)\n                                 for col_name in column._columns])\n            else:\n                grouping.append(column)\n        self._group_by = grouping\n\n    def group_by_extend(self, *values):\n        \"\"\"@Node.copy used from group_by() call\"\"\"\n        group_by = tuple(self._group_by or ()) + values\n        return self.group_by(*group_by)\n\n    @Node.copy\n    def having(self, *expressions):\n        if self._having is not None:\n            expressions = (self._having,) + expressions\n        self._having = reduce(operator.and_, expressions)\n\n    @Node.copy\n    def distinct(self, *columns):\n        if len(columns) == 1 and (columns[0] is True or columns[0] is False):\n            self._simple_distinct = columns[0]\n        else:\n            self._simple_distinct = False\n            self._distinct = columns\n\n    @Node.copy\n    def window(self, *windows):\n        self._windows = windows if windows else None\n\n    @Node.copy\n    def for_update(self, for_update=True, of=None, nowait=None,\n                   skip_locked=None):\n        if not for_update and (of is not None or nowait or skip_locked):\n            for_update = True\n\n        if not for_update:\n            self._for_update = None\n        else:\n            self._for_update = ForUpdate(for_update, of, nowait, skip_locked)\n\n    @Node.copy\n    def lateral(self, lateral=True):\n        self._lateral = lateral\n\n    def _get_query_key(self):\n        return self._alias\n\n    def __sql_selection__(self, ctx, is_subquery=False):\n        return ctx.sql(CommaNodeList(self._returning))\n\n    def __sql__(self, ctx):\n        if ctx.scope == SCOPE_COLUMN:\n            return self.apply_column(ctx)\n\n        if self._lateral and ctx.scope == SCOPE_SOURCE:\n            ctx.literal('LATERAL ')\n\n        is_subquery = ctx.subquery\n        state = {\n            'converter': None,\n            'in_function': False,\n            'parentheses': is_subquery or (ctx.scope == SCOPE_SOURCE),\n            'subquery': True,\n        }\n        if ctx.state.in_function and ctx.state.function_arg_count == 1:\n            state['parentheses'] = False\n\n        with ctx.scope_normal(**state):\n            # Defer calling parent SQL until here. This ensures that any CTEs\n            # for this query will be properly nested if this query is a\n            # sub-select or is used in an expression. See GH#1809 for example.\n            super(Select, self).__sql__(ctx)\n\n            ctx.literal('SELECT ')\n            if self._simple_distinct or self._distinct is not None:\n                ctx.literal('DISTINCT ')\n                if self._distinct:\n                    (ctx\n                     .literal('ON ')\n                     .sql(EnclosedNodeList(self._distinct))\n                     .literal(' '))\n\n            with ctx.scope_source():\n                ctx = self.__sql_selection__(ctx, is_subquery)\n\n            if self._from_list:\n                with ctx.scope_source(parentheses=False):\n                    ctx.literal(' FROM ').sql(CommaNodeList(self._from_list))\n\n            if self._where is not None:\n                ctx.literal(' WHERE ').sql(self._where)\n\n            if self._group_by:\n                ctx.literal(' GROUP BY ').sql(CommaNodeList(self._group_by))\n\n            if self._having is not None:\n                ctx.literal(' HAVING ').sql(self._having)\n\n            if self._windows is not None:\n                ctx.literal(' WINDOW ')\n                ctx.sql(CommaNodeList(self._windows))\n\n            # Apply ORDER BY, LIMIT, OFFSET.\n            self._apply_ordering(ctx)\n\n            if self._for_update is not None:\n                if not ctx.state.for_update:\n                    raise ValueError('FOR UPDATE specified but not supported '\n                                     'by database.')\n                ctx.literal(' ')\n                ctx.sql(self._for_update)\n\n        # If the subquery is inside a function -or- we are evaluating a\n        # subquery on either side of an expression w/o an explicit alias, do\n        # not generate an alias + AS clause.\n        if ctx.state.in_function or (ctx.state.in_expr and\n                                     self._alias is None):\n            return ctx\n\n        return self.apply_alias(ctx)\n\n\nclass _WriteQuery(Query):\n    def __init__(self, table, returning=None, **kwargs):\n        self.table = table\n        self._returning = returning\n        self._return_cursor = True if returning else False\n        super(_WriteQuery, self).__init__(**kwargs)\n\n    def cte(self, name, recursive=False, columns=None, materialized=None):\n        return CTE(name, self, recursive=recursive, columns=columns,\n                   materialized=materialized)\n\n    @Node.copy\n    def returning(self, *returning):\n        self._returning = returning\n        self._return_cursor = True if returning else False\n\n    def apply_returning(self, ctx):\n        if self._returning:\n            with ctx.scope_source():\n                ctx.literal(' RETURNING ').sql(CommaNodeList(self._returning))\n        return ctx\n\n    def _execute(self, database):\n        if self._returning:\n            cursor = self.execute_returning(database)\n        else:\n            cursor = database.execute(self)\n        return self.handle_result(database, cursor)\n\n    def execute_returning(self, database):\n        if self._cursor_wrapper is None:\n            cursor = database.execute(self)\n            self._cursor_wrapper = self._get_cursor_wrapper(cursor)\n        return self._cursor_wrapper\n\n    def handle_result(self, database, cursor):\n        if self._return_cursor:\n            return cursor\n        return database.rows_affected(cursor)\n\n    def _set_table_alias(self, ctx):\n        ctx.alias_manager[self.table] = self.table.__name__\n\n    def __sql__(self, ctx):\n        super(_WriteQuery, self).__sql__(ctx)\n        # We explicitly set the table alias to the table's name, which ensures\n        # that if a sub-select references a column on the outer table, we won't\n        # assign it a new alias (e.g. t2) but will refer to it as table.column.\n        self._set_table_alias(ctx)\n        return ctx\n\n\nclass Update(_WriteQuery):\n    def __init__(self, table, update=None, **kwargs):\n        super(Update, self).__init__(table, **kwargs)\n        self._update = update\n        self._from = None\n\n    @Node.copy\n    def from_(self, *sources):\n        self._from = sources\n\n    def __sql__(self, ctx):\n        super(Update, self).__sql__(ctx)\n\n        with ctx.scope_values(subquery=True):\n            ctx.literal('UPDATE ')\n\n            expressions = []\n            for k, v in sorted(self._update.items(), key=ctx.column_sort_key):\n                if not isinstance(v, Node):\n                    if isinstance(k, Field):\n                        v = k.to_value(v)\n                    else:\n                        v = Value(v, unpack=False)\n                elif isinstance(v, Model) and isinstance(k, ForeignKeyField):\n                    # NB: we want to ensure that when passed a model instance\n                    # in the context of a foreign-key, we apply the fk-specific\n                    # adaptation of the model.\n                    v = k.to_value(v)\n\n                if not isinstance(v, Value):\n                    v = qualify_names(v)\n\n                expressions.append(NodeList((k, SQL('='), v)))\n\n            (ctx\n             .sql(self.table)\n             .literal(' SET ')\n             .sql(CommaNodeList(expressions)))\n\n            if self._from:\n                with ctx.scope_source(parentheses=False):\n                    ctx.literal(' FROM ').sql(CommaNodeList(self._from))\n\n            if self._where:\n                with ctx.scope_normal():\n                    ctx.literal(' WHERE ').sql(self._where)\n            self._apply_ordering(ctx)\n            return self.apply_returning(ctx)\n\n\nclass Insert(_WriteQuery):\n    SIMPLE = 0\n    QUERY = 1\n    MULTI = 2\n    class DefaultValuesException(Exception): pass\n\n    def __init__(self, table, insert=None, columns=None, on_conflict=None,\n                 **kwargs):\n        super(Insert, self).__init__(table, **kwargs)\n        self._insert = insert\n        self._columns = columns\n        self._on_conflict = on_conflict\n        self._query_type = None\n        self._as_rowcount = False\n\n    def where(self, *expressions):\n        raise NotImplementedError('INSERT queries cannot have a WHERE clause.')\n\n    @Node.copy\n    def as_rowcount(self, _as_rowcount=True):\n        self._as_rowcount = _as_rowcount\n\n    @Node.copy\n    def on_conflict_ignore(self, ignore=True):\n        self._on_conflict = OnConflict('IGNORE') if ignore else None\n\n    @Node.copy\n    def on_conflict_replace(self, replace=True):\n        self._on_conflict = OnConflict('REPLACE') if replace else None\n\n    @Node.copy\n    def on_conflict(self, *args, **kwargs):\n        self._on_conflict = (OnConflict(*args, **kwargs) if (args or kwargs)\n                             else None)\n\n    def _simple_insert(self, ctx):\n        if not self._insert:\n            raise self.DefaultValuesException('Error: no data to insert.')\n        return self._generate_insert((self._insert,), ctx)\n\n    def get_default_data(self):\n        return {}\n\n    def get_default_columns(self):\n        if self.table._columns:\n            return [getattr(self.table, col) for col in self.table._columns\n                    if col != self.table._primary_key]\n\n    def _generate_insert(self, insert, ctx):\n        rows_iter = iter(insert)\n        columns = self._columns\n\n        # Load and organize column defaults (if provided).\n        defaults = self.get_default_data()\n\n        # First figure out what columns are being inserted (if they weren't\n        # specified explicitly). Resulting columns are normalized and ordered.\n        if not columns:\n            try:\n                row = next(rows_iter)\n            except StopIteration:\n                raise self.DefaultValuesException('Error: no rows to insert.')\n\n            if not isinstance(row, Mapping):\n                columns = self.get_default_columns()\n                if columns is None:\n                    raise ValueError('Bulk insert must specify columns.')\n            else:\n                # Infer column names from the dict of data being inserted.\n                accum = []\n                for column in row:\n                    if isinstance(column, str):\n                        column = getattr(self.table, column)\n                    accum.append(column)\n\n                # Add any columns present in the default data that are not\n                # accounted for by the dictionary of row data.\n                column_set = set(accum)\n                for col in (set(defaults) - column_set):\n                    accum.append(col)\n\n                columns = sorted(accum, key=lambda obj: obj.get_sort_key(ctx))\n            rows_iter = itertools.chain(iter((row,)), rows_iter)\n        else:\n            clean_columns = []\n            seen = set()\n            for column in columns:\n                if isinstance(column, str):\n                    column_obj = getattr(self.table, column)\n                else:\n                    column_obj = column\n                clean_columns.append(column_obj)\n                seen.add(column_obj)\n\n            columns = clean_columns\n            for col in sorted(defaults, key=lambda obj: obj.get_sort_key(ctx)):\n                if col not in seen:\n                    columns.append(col)\n\n        fk_fields = set()\n        nullable_columns = set()\n        value_lookups = {}\n        for column in columns:\n            lookups = [column, column.name]\n            if isinstance(column, Field):\n                if column.name != column.column_name:\n                    lookups.append(column.column_name)\n                if column.null:\n                    nullable_columns.add(column)\n                if isinstance(column, ForeignKeyField):\n                    fk_fields.add(column)\n            value_lookups[column] = lookups\n\n        ctx.sql(EnclosedNodeList(columns)).literal(' VALUES ')\n        columns_converters = [\n            (column, column.db_value if isinstance(column, Field) else None)\n            for column in columns]\n\n        all_values = []\n        for row in rows_iter:\n            values = []\n            is_dict = isinstance(row, Mapping)\n            for i, (column, converter) in enumerate(columns_converters):\n                try:\n                    if is_dict:\n                        # The logic is a bit convoluted, but in order to be\n                        # flexible in what we accept (dict keyed by\n                        # column/field, field name, or underlying column name),\n                        # we try accessing the row data dict using each\n                        # possible key. If no match is found, throw an error.\n                        for lookup in value_lookups[column]:\n                            try:\n                                val = row[lookup]\n                            except KeyError: pass\n                            else: break\n                        else:\n                            raise KeyError\n                    else:\n                        val = row[i]\n                except (KeyError, IndexError):\n                    if column in defaults:\n                        val = defaults[column]\n                        if callable_(val):\n                            val = val()\n                    elif column in nullable_columns:\n                        val = None\n                    else:\n                        raise ValueError('Missing value for %s.' % column.name)\n\n                if not isinstance(val, Node) or (isinstance(val, Model) and\n                                                 column in fk_fields):\n                    val = Value(val, converter=converter, unpack=False)\n                values.append(val)\n\n            all_values.append(EnclosedNodeList(values))\n\n        if not all_values:\n            raise self.DefaultValuesException('Error: no data to insert.')\n\n        with ctx.scope_values(subquery=True):\n            return ctx.sql(CommaNodeList(all_values))\n\n    def _query_insert(self, ctx):\n        return (ctx\n                .sql(EnclosedNodeList(self._columns))\n                .literal(' ')\n                .sql(self._insert))\n\n    def _default_values(self, ctx):\n        if not self._database:\n            return ctx.literal('DEFAULT VALUES')\n        return self._database.default_values_insert(ctx)\n\n    def __sql__(self, ctx):\n        super(Insert, self).__sql__(ctx)\n        with ctx.scope_values():\n            stmt = None\n            if self._on_conflict is not None:\n                stmt = self._on_conflict.get_conflict_statement(ctx, self)\n\n            (ctx\n             .sql(stmt or SQL('INSERT'))\n             .literal(' INTO ')\n             .sql(self.table)\n             .literal(' '))\n\n            if isinstance(self._insert, Mapping) and not self._columns:\n                try:\n                    self._simple_insert(ctx)\n                except self.DefaultValuesException:\n                    self._default_values(ctx)\n                self._query_type = Insert.SIMPLE\n            elif isinstance(self._insert, (SelectQuery, SQL)):\n                self._query_insert(ctx)\n                self._query_type = Insert.QUERY\n            else:\n                self._generate_insert(self._insert, ctx)\n                self._query_type = Insert.MULTI\n\n            if self._on_conflict is not None:\n                update = self._on_conflict.get_conflict_update(ctx, self)\n                if update is not None:\n                    ctx.literal(' ').sql(update)\n\n            return self.apply_returning(ctx)\n\n    def _execute(self, database):\n        if self._returning is None and database.returning_clause \\\n           and self.table._primary_key:\n            self._returning = (self.table._primary_key,)\n        try:\n            return super(Insert, self)._execute(database)\n        except self.DefaultValuesException:\n            pass\n\n    def handle_result(self, database, cursor):\n        if self._return_cursor:\n            return cursor\n        if self._as_rowcount:\n            return database.rows_affected(cursor)\n        return database.last_insert_id(cursor, self._query_type)\n\n\nclass Delete(_WriteQuery):\n    def __sql__(self, ctx):\n        super(Delete, self).__sql__(ctx)\n\n        with ctx.scope_values(subquery=True):\n            ctx.literal('DELETE FROM ').sql(self.table)\n            if self._where is not None:\n                with ctx.scope_normal():\n                    ctx.literal(' WHERE ').sql(self._where)\n\n            self._apply_ordering(ctx)\n            return self.apply_returning(ctx)\n\n\nclass Index(Node):\n    def __init__(self, name, table, expressions, unique=False, safe=False,\n                 where=None, using=None, nulls_distinct=None):\n        self._name = name\n        self._table = Entity(table) if not isinstance(table, Table) else table\n        self._expressions = expressions\n        self._where = where\n        self._unique = unique\n        self._safe = safe\n        self._using = using\n        self._nulls_distinct = nulls_distinct\n        if self._nulls_distinct is not None and not self._unique:\n            raise ValueError('NULLS DISTINCT is only available with UNIQUE.')\n\n    @Node.copy\n    def safe(self, _safe=True):\n        self._safe = _safe\n\n    @Node.copy\n    def where(self, *expressions):\n        if self._where is not None:\n            expressions = (self._where,) + expressions\n        self._where = reduce(operator.and_, expressions)\n\n    @Node.copy\n    def using(self, _using=None):\n        self._using = _using\n\n    @Node.copy\n    def nulls_distinct(self, nulls_distinct=None):\n        if nulls_distinct is not None and not self._unique:\n            raise ValueError('NULLS DISTINCT is only available with UNIQUE.')\n        self._nulls_distinct = nulls_distinct\n\n    def __sql__(self, ctx):\n        statement = 'CREATE UNIQUE INDEX ' if self._unique else 'CREATE INDEX '\n        with ctx.scope_values(subquery=True):\n            ctx.literal(statement)\n            if self._safe:\n                ctx.literal('IF NOT EXISTS ')\n\n            # Sqlite uses CREATE INDEX <schema>.<name> ON <table>, whereas most\n            # others use: CREATE INDEX <name> ON <schema>.<table>.\n            if ctx.state.index_schema_prefix and \\\n               isinstance(self._table, Table) and self._table._schema:\n                index_name = Entity(self._table._schema, self._name)\n                table_name = Entity(self._table.__name__)\n            else:\n                index_name = Entity(self._name)\n                table_name = self._table\n\n            ctx.sql(index_name)\n            if self._using is not None and \\\n               ctx.state.index_using_precedes_table:\n                ctx.literal(' USING %s' % self._using)  # MySQL style.\n\n            (ctx\n             .literal(' ON ')\n             .sql(table_name)\n             .literal(' '))\n\n            if self._using is not None and not \\\n               ctx.state.index_using_precedes_table:\n                ctx.literal('USING %s ' % self._using)  # Postgres/default.\n\n            ctx.sql(EnclosedNodeList([\n                SQL(expr) if isinstance(expr, str) else expr\n                for expr in self._expressions]))\n            if self._where is not None:\n                ctx.literal(' WHERE ').sql(self._where)\n\n            if self._nulls_distinct is not None:\n                ctx.literal(' NULLS DISTINCT' if self._nulls_distinct else\n                            ' NULLS NOT DISTINCT')\n\n        return ctx\n\n\nclass ModelIndex(Index):\n    def __init__(self, model, fields, unique=False, safe=True, where=None,\n                 using=None, name=None, nulls_distinct=None):\n        self._model = model\n        if name is None:\n            name = self._generate_name_from_fields(model, fields)\n        if using is None:\n            for field in fields:\n                if isinstance(field, Field) and hasattr(field, 'index_type'):\n                    using = field.index_type\n        super(ModelIndex, self).__init__(\n            name=name,\n            table=model._meta.table,\n            expressions=fields,\n            unique=unique,\n            safe=safe,\n            where=where,\n            using=using,\n            nulls_distinct=nulls_distinct)\n\n    def _generate_name_from_fields(self, model, fields):\n        accum = []\n        for field in fields:\n            if isinstance(field, str):\n                accum.append(field.split()[0])\n            else:\n                if isinstance(field, Node) and not isinstance(field, Field):\n                    field = field.unwrap()\n                if isinstance(field, Field):\n                    accum.append(field.column_name)\n\n        if not accum:\n            raise ValueError('Unable to generate a name for the index, please '\n                             'explicitly specify a name.')\n\n        clean_field_names = re.sub(r'[^\\w]+', '', '_'.join(accum))\n        meta = model._meta\n        prefix = meta.name if meta.legacy_table_names else meta.table_name\n        return _truncate_constraint_name('_'.join((prefix, clean_field_names)))\n\n\ndef _truncate_constraint_name(constraint, maxlen=64):\n    if len(constraint) > maxlen:\n        name_hash = hashlib.md5(constraint.encode('utf-8')).hexdigest()\n        constraint = '%s_%s' % (constraint[:(maxlen - 8)], name_hash[:7])\n    return constraint\n\n\n# DB-API 2.0 EXCEPTIONS.\n\n\nclass PeeweeException(Exception):\n    def __init__(self, *args):\n        if args and isinstance(args[0], Exception):\n            self.orig, args = args[0], args[1:]\n        super(PeeweeException, self).__init__(*args)\nclass ImproperlyConfigured(PeeweeException): pass\nclass DatabaseError(PeeweeException): pass\nclass DataError(DatabaseError): pass\nclass IntegrityError(DatabaseError): pass\nclass InterfaceError(PeeweeException): pass\nclass InternalError(DatabaseError): pass\nclass NotSupportedError(DatabaseError): pass\nclass OperationalError(DatabaseError): pass\nclass ProgrammingError(DatabaseError): pass\n\n\nclass ExceptionWrapper(object):\n    __slots__ = ('exceptions',)\n    def __init__(self, exceptions):\n        self.exceptions = exceptions\n    def __enter__(self): pass\n    def __exit__(self, exc_type, exc_value, traceback):\n        if exc_type is None:\n            return\n        # psycopg shits out a million cute error types. Try to catch em all.\n        if pg_errors is not None and exc_type.__name__ not in self.exceptions \\\n           and issubclass(exc_type, pg_errors.Error):\n            exc_type = exc_type.__bases__[0]\n        elif pg3_errors is not None and \\\n           exc_type.__name__ not in self.exceptions \\\n           and issubclass(exc_type, pg3_errors.Error):\n            exc_type = exc_type.__bases__[0]\n        if exc_type.__name__ in self.exceptions:\n            new_type = self.exceptions[exc_type.__name__]\n            exc_args = exc_value.args\n            reraise(new_type, new_type(exc_value, *exc_args), traceback)\n\n\nEXCEPTIONS = {\n    'ConstraintError': IntegrityError,\n    'DatabaseError': DatabaseError,\n    'DataError': DataError,\n    'IntegrityError': IntegrityError,\n    'InterfaceError': InterfaceError,\n    'InternalError': InternalError,\n    'NotSupportedError': NotSupportedError,\n    'OperationalError': OperationalError,\n    'ProgrammingError': ProgrammingError,\n    'TransactionRollbackError': OperationalError,\n    'UndefinedFunction': ProgrammingError,\n    'UniqueViolation': IntegrityError}\n\n__exception_wrapper__ = ExceptionWrapper(EXCEPTIONS)\n\n\n# DATABASE INTERFACE AND CONNECTION MANAGEMENT.\n\n\nIndexMetadata = collections.namedtuple(\n    'IndexMetadata',\n    ('name', 'sql', 'columns', 'unique', 'table'))\nColumnMetadata = collections.namedtuple(\n    'ColumnMetadata',\n    ('name', 'data_type', 'null', 'primary_key', 'table', 'default'))\nForeignKeyMetadata = collections.namedtuple(\n    'ForeignKeyMetadata',\n    ('column', 'dest_table', 'dest_column', 'table'))\nViewMetadata = collections.namedtuple('ViewMetadata', ('name', 'sql'))\n\n\nclass _ConnectionState(object):\n    def __init__(self, **kwargs):\n        super(_ConnectionState, self).__init__(**kwargs)\n        self.reset()\n\n    def reset(self):\n        self.closed = True\n        self.conn = None\n        self.ctx = []\n        self.transactions = []\n\n    def set_connection(self, conn):\n        self.conn = conn\n        self.closed = False\n        self.ctx = []\n        self.transactions = []\n\n\nclass _ConnectionLocal(_ConnectionState, threading.local): pass\nclass _NoopLock(object):\n    __slots__ = ()\n    def __enter__(self): return self\n    def __exit__(self, exc_type, exc_val, exc_tb): pass\n\n\nclass ConnectionContext(object):\n    __slots__ = ('db',)\n    def __init__(self, db): self.db = db\n    def __enter__(self):\n        if self.db.is_closed():\n            self.db.connect()\n    def __exit__(self, exc_type, exc_val, exc_tb): self.db.close()\n    def __call__(self, fn):\n        @wraps(fn)\n        def inner(*args, **kwargs):\n            with ConnectionContext(self.db):\n                return fn(*args, **kwargs)\n        return inner\n\n\nclass Database(_callable_context_manager):\n    context_class = Context\n    field_types = {}\n    operations = {}\n    param = '?'\n    quote = '\"\"'\n    server_version = None\n\n    # Feature toggles.\n    compound_select_parentheses = CSQ_PARENTHESES_NEVER\n    for_update = False\n    index_schema_prefix = False\n    index_using_precedes_table = False\n    limit_max = None\n    nulls_ordering = False\n    returning_clause = False\n    safe_create_index = True\n    safe_drop_index = True\n    sequences = False\n    truncate_table = True\n\n    def __init__(self, database, thread_safe=True, autorollback=False,\n                 field_types=None, operations=None, autocommit=None,\n                 autoconnect=True, **kwargs):\n        self._field_types = merge_dict(FIELD, self.field_types)\n        self._operations = merge_dict(OP, self.operations)\n        if field_types:\n            self._field_types.update(field_types)\n        if operations:\n            self._operations.update(operations)\n\n        self.autoconnect = autoconnect\n        self.thread_safe = thread_safe\n        if thread_safe:\n            self._state = _ConnectionLocal()\n            self._lock = threading.Lock()\n        else:\n            self._state = _ConnectionState()\n            self._lock = _NoopLock()\n\n        if autorollback:\n            __deprecated__('Peewee no longer uses the \"autorollback\" option, '\n                           'as we always run in autocommit-mode now. This '\n                           'changes psycopg2\\'s semantics so that the conn '\n                           'is not left in a transaction-aborted state.')\n\n        if autocommit is not None:\n            __deprecated__('Peewee no longer uses the \"autocommit\" option, as '\n                           'the semantics now require it to always be True. '\n                           'Because some database-drivers also use the '\n                           '\"autocommit\" parameter, you are receiving a '\n                           'warning so you may update your code and remove '\n                           'the parameter, as in the future, specifying '\n                           'autocommit could impact the behavior of the '\n                           'database driver you are using.')\n\n        self.connect_params = {}\n        self.init(database, **kwargs)\n\n    def init(self, database, **kwargs):\n        if not self.is_closed():\n            self.close()\n        self.database = database\n        self.connect_params.update(kwargs)\n        self.deferred = not bool(database)\n\n    def __enter__(self):\n        if self.is_closed():\n            self.connect()\n        ctx = self.atomic()\n        self._state.ctx.append(ctx)\n        ctx.__enter__()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        ctx = self._state.ctx.pop()\n        try:\n            ctx.__exit__(exc_type, exc_val, exc_tb)\n        finally:\n            if not self._state.ctx:\n                self.close()\n\n    def connection_context(self):\n        return ConnectionContext(self)\n\n    def _connect(self):\n        raise NotImplementedError\n\n    def connect(self, reuse_if_open=False):\n        with self._lock:\n            if self.deferred:\n                raise InterfaceError('Error, database must be initialized '\n                                     'before opening a connection.')\n            if not self._state.closed:\n                if reuse_if_open:\n                    return False\n                raise OperationalError('Connection already opened.')\n\n            self._state.reset()\n            with __exception_wrapper__:\n                self._state.set_connection(self._connect())\n                if self.server_version is None:\n                    self._set_server_version(self._state.conn)\n                self._initialize_connection(self._state.conn)\n        return True\n\n    def _initialize_connection(self, conn):\n        pass\n\n    def _set_server_version(self, conn):\n        self.server_version = 0\n\n    def close(self):\n        with self._lock:\n            if self.deferred:\n                raise InterfaceError('Error, database must be initialized '\n                                     'before opening a connection.')\n            if self.in_transaction():\n                raise OperationalError('Attempting to close database while '\n                                       'transaction is open.')\n            is_open = not self._state.closed\n            try:\n                if is_open:\n                    with __exception_wrapper__:\n                        self._close(self._state.conn)\n            finally:\n                self._state.reset()\n            return is_open\n\n    def _close(self, conn):\n        conn.close()\n\n    def is_closed(self):\n        return self._state.closed\n\n    def is_connection_usable(self):\n        return not self._state.closed\n\n    def connection(self):\n        if self.is_closed():\n            self.connect()\n        return self._state.conn\n\n    def cursor(self, named_cursor=None):\n        if self.is_closed():\n            if self.autoconnect:\n                self.connect()\n            else:\n                raise InterfaceError('Error, database connection not opened.')\n        return self._state.conn.cursor()\n\n    def execute_sql(self, sql, params=None):\n        if logger.isEnabledFor(logging.DEBUG):\n            logger.debug((sql, params))\n        with __exception_wrapper__:\n            cursor = self.cursor()\n            cursor.execute(sql, params or ())\n        return cursor\n\n    def execute(self, query, **context_options):\n        ctx = self.get_sql_context(**context_options)\n        sql, params = ctx.sql(query).query()\n        return self.execute_sql(sql, params)\n\n    def get_context_options(self):\n        return {\n            'field_types': self._field_types,\n            'operations': self._operations,\n            'param': self.param,\n            'quote': self.quote,\n            'compound_select_parentheses': self.compound_select_parentheses,\n            'conflict_statement': self.conflict_statement,\n            'conflict_update': self.conflict_update,\n            'for_update': self.for_update,\n            'index_schema_prefix': self.index_schema_prefix,\n            'index_using_precedes_table': self.index_using_precedes_table,\n            'limit_max': self.limit_max,\n            'nulls_ordering': self.nulls_ordering,\n        }\n\n    def get_sql_context(self, **context_options):\n        context = self.get_context_options()\n        if context_options:\n            context.update(context_options)\n        return self.context_class(**context)\n\n    def conflict_statement(self, on_conflict, query):\n        raise NotImplementedError\n\n    def conflict_update(self, on_conflict, query):\n        raise NotImplementedError\n\n    def _build_on_conflict_update(self, on_conflict, query):\n        if on_conflict._conflict_target:\n            stmt = SQL('ON CONFLICT')\n            target = EnclosedNodeList([\n                Entity(col) if isinstance(col, str) else col\n                for col in on_conflict._conflict_target])\n            if on_conflict._conflict_where is not None:\n                target = NodeList([target, SQL('WHERE'),\n                                   on_conflict._conflict_where])\n        else:\n            stmt = SQL('ON CONFLICT ON CONSTRAINT')\n            target = on_conflict._conflict_constraint\n            if isinstance(target, str):\n                target = Entity(target)\n\n        updates = []\n        if on_conflict._preserve:\n            for column in on_conflict._preserve:\n                excluded = NodeList((SQL('EXCLUDED'), ensure_entity(column)),\n                                    glue='.')\n                expression = NodeList((ensure_entity(column), SQL('='),\n                                       excluded))\n                updates.append(expression)\n\n        if on_conflict._update:\n            for k, v in on_conflict._update.items():\n                if not isinstance(v, Node):\n                    # Attempt to resolve string field-names to their respective\n                    # field object, to apply data-type conversions.\n                    if isinstance(k, str):\n                        k = getattr(query.table, k)\n                    if isinstance(k, Field):\n                        v = k.to_value(v)\n                    else:\n                        v = Value(v, unpack=False)\n                else:\n                    v = QualifiedNames(v)\n                updates.append(NodeList((ensure_entity(k), SQL('='), v)))\n\n        parts = [stmt, target, SQL('DO UPDATE SET'), CommaNodeList(updates)]\n        if on_conflict._where:\n            parts.extend((SQL('WHERE'), QualifiedNames(on_conflict._where)))\n\n        return NodeList(parts)\n\n    def last_insert_id(self, cursor, query_type=None):\n        return cursor.lastrowid\n\n    def rows_affected(self, cursor):\n        return cursor.rowcount\n\n    def default_values_insert(self, ctx):\n        return ctx.literal('DEFAULT VALUES')\n\n    def session_start(self):\n        return self.transaction().__enter__()\n\n    def session_commit(self):\n        try:\n            txn = self.pop_transaction()\n        except IndexError:\n            return False\n        txn.commit(begin=self.in_transaction())\n        return True\n\n    def session_rollback(self):\n        try:\n            txn = self.pop_transaction()\n        except IndexError:\n            return False\n        txn.rollback(begin=self.in_transaction())\n        return True\n\n    def in_transaction(self):\n        return bool(self._state.transactions)\n\n    def push_transaction(self, transaction):\n        self._state.transactions.append(transaction)\n\n    def pop_transaction(self):\n        return self._state.transactions.pop()\n\n    def transaction_depth(self):\n        return len(self._state.transactions)\n\n    def top_transaction(self):\n        if self._state.transactions:\n            return self._state.transactions[-1]\n\n    def atomic(self, *args, **kwargs):\n        return _atomic(self, *args, **kwargs)\n\n    def manual_commit(self):\n        return _manual(self)\n\n    def transaction(self, *args, **kwargs):\n        return _transaction(self, *args, **kwargs)\n\n    def savepoint(self):\n        return _savepoint(self)\n\n    def begin(self):\n        if self.is_closed():\n            self.connect()\n        with __exception_wrapper__:\n            self.cursor().execute('BEGIN')\n\n    def rollback(self):\n        with __exception_wrapper__:\n            self.cursor().execute('ROLLBACK')\n\n    def commit(self):\n        with __exception_wrapper__:\n            self.cursor().execute('COMMIT')\n\n    def batch_commit(self, it, n):\n        for group in chunked(it, n):\n            with self.atomic():\n                for obj in group:\n                    yield obj\n\n    def table_exists(self, table_name, schema=None):\n        if is_model(table_name):\n            model = table_name\n            table_name = model._meta.table_name\n            schema = model._meta.schema\n        return table_name in self.get_tables(schema=schema)\n\n    def get_tables(self, schema=None):\n        raise NotImplementedError\n\n    def get_indexes(self, table, schema=None):\n        raise NotImplementedError\n\n    def get_columns(self, table, schema=None):\n        raise NotImplementedError\n\n    def get_primary_keys(self, table, schema=None):\n        raise NotImplementedError\n\n    def get_foreign_keys(self, table, schema=None):\n        raise NotImplementedError\n\n    def sequence_exists(self, seq):\n        raise NotImplementedError\n\n    def create_tables(self, models, **options):\n        for model in sort_models(models):\n            model.create_table(**options)\n\n    def drop_tables(self, models, **kwargs):\n        for model in reversed(sort_models(models)):\n            model.drop_table(**kwargs)\n\n    def extract_date(self, date_part, date_field):\n        raise NotImplementedError\n\n    def truncate_date(self, date_part, date_field):\n        raise NotImplementedError\n\n    def to_timestamp(self, date_field):\n        raise NotImplementedError\n\n    def from_timestamp(self, date_field):\n        raise NotImplementedError\n\n    def random(self):\n        return fn.random()\n\n    def bind(self, models, bind_refs=True, bind_backrefs=True):\n        for model in models:\n            model.bind(self, bind_refs=bind_refs, bind_backrefs=bind_backrefs)\n\n    def bind_ctx(self, models, bind_refs=True, bind_backrefs=True):\n        return _BoundModelsContext(models, self, bind_refs, bind_backrefs)\n\n    def get_noop_select(self, ctx):\n        return ctx.sql(Select().columns(SQL('0')).where(SQL('0')))\n\n    @property\n    def Model(self):\n        if not hasattr(self, '_Model'):\n            class Meta: database = self\n            self._Model = type('BaseModel', (Model,), {'Meta': Meta})\n        return self._Model\n\n\ndef __pragma__(name):\n    def __get__(self):\n        return self.pragma(name)\n    def __set__(self, value):\n        return self.pragma(name, value)\n    return property(__get__, __set__)\n\n\nclass SqliteDatabase(Database):\n    field_types = {\n        'BIGAUTO': FIELD.AUTO,\n        'BIGINT': FIELD.INT,\n        'BOOL': FIELD.INT,\n        'DOUBLE': FIELD.FLOAT,\n        'SMALLINT': FIELD.INT,\n        'UUID': FIELD.TEXT}\n    operations = {\n        'LIKE': 'GLOB',\n        'ILIKE': 'LIKE'}\n    index_schema_prefix = True\n    limit_max = -1\n    server_version = __sqlite_version__\n    truncate_table = False\n\n    def __init__(self, database, pragmas=None, regexp_function=False,\n                 rank_functions=False, *args, **kwargs):\n        isolation = kwargs.pop('isolation_level', None)\n        if isolation is not None:\n            raise ImproperlyConfigured('isolation_level must be None when '\n                                       'using peewee.')\n\n        self._pragmas = pragmas or ()\n        super(SqliteDatabase, self).__init__(database, *args, **kwargs)\n        self._aggregates = {}\n        self._collations = {}\n        self._functions = {}\n        self._window_functions = {}\n        self._extensions = set()\n        self._attached = {}\n        self.nulls_ordering = self.server_version >= (3, 30, 0)\n        self.register_function(_sqlite_date_part, 'date_part', 2)\n        self.register_function(_sqlite_date_trunc, 'date_trunc', 2)\n        if regexp_function:\n            self.register_function(_sqlite_regexp, 'regexp', 2)\n        if rank_functions:\n            from playhouse.sqlite_udf import register_udf_groups, RANK\n            register_udf_groups(self, RANK)\n\n    def init(self, database, pragmas=None, timeout=5, returning_clause=None,\n             **kwargs):\n        if pragmas is not None:\n            self._pragmas = pragmas\n        if isinstance(self._pragmas, dict):\n            self._pragmas = list(self._pragmas.items())\n        if returning_clause is not None:\n            if __sqlite_version__ < (3, 35, 0):\n                warnings.warn('RETURNING clause requires Sqlite 3.35 or newer')\n            self.returning_clause = returning_clause\n        self._timeout = timeout\n        super(SqliteDatabase, self).init(database, **kwargs)\n\n    def _set_server_version(self, conn):\n        pass\n\n    def _connect(self):\n        if sqlite3 is None:\n            raise ImproperlyConfigured('SQLite driver not installed!')\n        conn = sqlite3.connect(self.database, timeout=self._timeout,\n                               isolation_level=None, **self.connect_params)\n        try:\n            self._add_conn_hooks(conn)\n        except Exception:\n            conn.close()\n            raise\n        return conn\n\n    def _add_conn_hooks(self, conn):\n        if self._attached:\n            self._attach_databases(conn)\n        if self._pragmas:\n            self._set_pragmas(conn)\n        self._load_aggregates(conn)\n        self._load_collations(conn)\n        self._load_functions(conn)\n        if self.server_version >= (3, 25, 0):\n            self._load_window_functions(conn)\n        if self._extensions:\n            self._load_extensions(conn)\n\n    def _set_pragmas(self, conn):\n        cursor = conn.cursor()\n        for pragma, value in self._pragmas:\n            cursor.execute('PRAGMA %s = %s;' % (pragma, value))\n        cursor.close()\n\n    def _attach_databases(self, conn):\n        cursor = conn.cursor()\n        for name, db in self._attached.items():\n            cursor.execute('ATTACH DATABASE \"%s\" AS \"%s\"' % (db, name))\n        cursor.close()\n\n    def pragma(self, key, value=SENTINEL, permanent=False, schema=None):\n        if schema is not None:\n            key = '\"%s\".%s' % (schema, key)\n        sql = 'PRAGMA %s' % key\n        if value is not SENTINEL:\n            sql += ' = %s' % (value or 0)\n            if permanent:\n                pragmas = dict(self._pragmas or ())\n                pragmas[key] = value\n                self._pragmas = list(pragmas.items())\n        elif permanent:\n            raise ValueError('Cannot specify a permanent pragma without value')\n        row = self.execute_sql(sql).fetchone()\n        if row:\n            return row[0]\n\n    cache_size = __pragma__('cache_size')\n    foreign_keys = __pragma__('foreign_keys')\n    journal_mode = __pragma__('journal_mode')\n    journal_size_limit = __pragma__('journal_size_limit')\n    mmap_size = __pragma__('mmap_size')\n    page_size = __pragma__('page_size')\n    read_uncommitted = __pragma__('read_uncommitted')\n    synchronous = __pragma__('synchronous')\n    wal_autocheckpoint = __pragma__('wal_autocheckpoint')\n    application_id = __pragma__('application_id')\n    user_version = __pragma__('user_version')\n    data_version = __pragma__('data_version')\n\n    @property\n    def timeout(self):\n        return self._timeout\n\n    @timeout.setter\n    def timeout(self, seconds):\n        if self._timeout == seconds:\n            return\n\n        self._timeout = seconds\n        if not self.is_closed():\n            # PySQLite multiplies user timeout by 1000, but the unit of the\n            # timeout PRAGMA is actually milliseconds.\n            self.execute_sql('PRAGMA busy_timeout=%d;' % (seconds * 1000))\n\n    def _load_aggregates(self, conn):\n        for name, (klass, num_params) in self._aggregates.items():\n            conn.create_aggregate(name, num_params, klass)\n\n    def _load_collations(self, conn):\n        for name, fn in self._collations.items():\n            conn.create_collation(name, fn)\n\n    def _load_functions(self, conn):\n        for name, (fn, n_params, deterministic) in self._functions.items():\n            kwargs = {'deterministic': deterministic} if deterministic else {}\n            conn.create_function(name, n_params, fn, **kwargs)\n\n    def _load_window_functions(self, conn):\n        for name, (klass, num_params) in self._window_functions.items():\n            conn.create_window_function(name, num_params, klass)\n\n    def register_aggregate(self, klass, name=None, num_params=-1):\n        self._aggregates[name or klass.__name__.lower()] = (klass, num_params)\n        if not self.is_closed():\n            self._load_aggregates(self.connection())\n\n    def aggregate(self, name=None, num_params=-1):\n        def decorator(klass):\n            self.register_aggregate(klass, name, num_params)\n            return klass\n        return decorator\n\n    def register_collation(self, fn, name=None):\n        name = name or fn.__name__\n        def _collation(*args):\n            expressions = args + (SQL('collate %s' % name),)\n            return NodeList(expressions)\n        fn.collation = _collation\n        self._collations[name] = fn\n        if not self.is_closed():\n            self._load_collations(self.connection())\n\n    def collation(self, name=None):\n        def decorator(fn):\n            self.register_collation(fn, name)\n            return fn\n        return decorator\n\n    def register_function(self, fn, name=None, num_params=-1,\n                          deterministic=None):\n        self._functions[name or fn.__name__] = (fn, num_params, deterministic)\n        if not self.is_closed():\n            self._load_functions(self.connection())\n\n    def func(self, name=None, num_params=-1, deterministic=None):\n        def decorator(fn):\n            self.register_function(fn, name, num_params, deterministic)\n            return fn\n        return decorator\n\n    def register_window_function(self, klass, name=None, num_params=-1):\n        name = name or klass.__name__.lower()\n        self._window_functions[name] = (klass, num_params)\n        if not self.is_closed():\n            self._load_window_functions(self.connection())\n\n    def window_function(self, name=None, num_params=-1):\n        def decorator(klass):\n            self.register_window_function(klass, name, num_params)\n            return klass\n        return decorator\n\n    def unregister_aggregate(self, name):\n        del(self._aggregates[name])\n\n    def unregister_collation(self, name):\n        del(self._collations[name])\n\n    def unregister_function(self, name):\n        del(self._functions[name])\n\n    def unregister_window_function(self, name):\n        del(self._window_functions[name])\n\n    def _load_extensions(self, conn):\n        conn.enable_load_extension(True)\n        for extension in self._extensions:\n            conn.load_extension(extension)\n\n    def load_extension(self, extension):\n        self._extensions.add(extension)\n        if not self.is_closed():\n            conn = self.connection()\n            conn.enable_load_extension(True)\n            conn.load_extension(extension)\n\n    def unload_extension(self, extension):\n        self._extensions.remove(extension)\n\n    def attach(self, filename, name):\n        if name in self._attached:\n            if self._attached[name] == filename:\n                return False\n            raise OperationalError('schema \"%s\" already attached.' % name)\n\n        self._attached[name] = filename\n        if not self.is_closed():\n            self.execute_sql('ATTACH DATABASE \"%s\" AS \"%s\"' % (filename, name))\n        return True\n\n    def detach(self, name):\n        if name not in self._attached:\n            return False\n\n        del self._attached[name]\n        if not self.is_closed():\n            self.execute_sql('DETACH DATABASE \"%s\"' % name)\n        return True\n\n    def last_insert_id(self, cursor, query_type=None):\n        if not self.returning_clause:\n            return cursor.lastrowid\n        elif query_type == Insert.SIMPLE:\n            try:\n                return cursor[0][0]\n            except (IndexError, KeyError, TypeError):\n                pass\n        return cursor\n\n    def rows_affected(self, cursor):\n        try:\n            return cursor.rowcount\n        except AttributeError:\n            return cursor.cursor.rowcount  # This was a RETURNING query.\n\n    def begin(self, lock_type=None):\n        statement = 'BEGIN %s' % lock_type if lock_type else 'BEGIN'\n        self.execute_sql(statement)\n\n    def commit(self):\n        with __exception_wrapper__:\n            return self.execute_sql('COMMIT')\n\n    def rollback(self):\n        with __exception_wrapper__:\n            return self.execute_sql('ROLLBACK')\n\n    def get_tables(self, schema=None):\n        schema = schema or 'main'\n        cursor = self.execute_sql('SELECT name FROM \"%s\".sqlite_master WHERE '\n                                  'type=? ORDER BY name' % schema, ('table',))\n        return [row for row, in cursor.fetchall()]\n\n    def get_views(self, schema=None):\n        sql = ('SELECT name, sql FROM \"%s\".sqlite_master WHERE type=? '\n               'ORDER BY name') % (schema or 'main')\n        return [ViewMetadata(*row) for row in self.execute_sql(sql, ('view',))]\n\n    def get_indexes(self, table, schema=None):\n        schema = schema or 'main'\n        query = ('SELECT name, sql FROM \"%s\".sqlite_master '\n                 'WHERE tbl_name = ? AND type = ? ORDER BY name') % schema\n        cursor = self.execute_sql(query, (table, 'index'))\n        index_to_sql = dict(cursor.fetchall())\n\n        # Determine which indexes have a unique constraint.\n        unique_indexes = set()\n        cursor = self.execute_sql('PRAGMA \"%s\".index_list(\"%s\")' %\n                                  (schema, table))\n        for row in cursor.fetchall():\n            name = row[1]\n            is_unique = int(row[2]) == 1\n            if is_unique:\n                unique_indexes.add(name)\n\n        # Retrieve the indexed columns.\n        index_columns = {}\n        for index_name in sorted(index_to_sql):\n            cursor = self.execute_sql('PRAGMA \"%s\".index_info(\"%s\")' %\n                                      (schema, index_name))\n            index_columns[index_name] = [row[2] for row in cursor.fetchall()]\n\n        return [\n            IndexMetadata(\n                name,\n                index_to_sql[name],\n                index_columns[name],\n                name in unique_indexes,\n                table)\n            for name in sorted(index_to_sql)]\n\n    def get_columns(self, table, schema=None):\n        cursor = self.execute_sql('PRAGMA \"%s\".table_info(\"%s\")' %\n                                  (schema or 'main', table))\n        return [ColumnMetadata(r[1], r[2], not r[3], bool(r[5]), table, r[4])\n                for r in cursor.fetchall()]\n\n    def get_primary_keys(self, table, schema=None):\n        cursor = self.execute_sql('PRAGMA \"%s\".table_info(\"%s\")' %\n                                  (schema or 'main', table))\n        return [row[1] for row in filter(lambda r: r[-1], cursor.fetchall())]\n\n    def get_foreign_keys(self, table, schema=None):\n        cursor = self.execute_sql('PRAGMA \"%s\".foreign_key_list(\"%s\")' %\n                                  (schema or 'main', table))\n        return [ForeignKeyMetadata(row[3], row[2], row[4], table)\n                for row in cursor.fetchall()]\n\n    def get_binary_type(self):\n        return sqlite3.Binary\n\n    def conflict_statement(self, on_conflict, query):\n        action = on_conflict._action.lower() if on_conflict._action else ''\n        if action and action not in ('nothing', 'update'):\n            return SQL('INSERT OR %s' % on_conflict._action.upper())\n\n    def conflict_update(self, oc, query):\n        # Sqlite prior to 3.24.0 does not support Postgres-style upsert.\n        if self.server_version < (3, 24, 0) and \\\n           any((oc._preserve, oc._update, oc._where, oc._conflict_target,\n                oc._conflict_constraint)):\n            raise ValueError('SQLite does not support specifying which values '\n                             'to preserve or update.')\n\n        action = oc._action.lower() if oc._action else ''\n        if action and action not in ('nothing', 'update', ''):\n            return\n\n        if action == 'nothing':\n            return SQL('ON CONFLICT DO NOTHING')\n        elif not oc._update and not oc._preserve:\n            raise ValueError('If you are not performing any updates (or '\n                             'preserving any INSERTed values), then the '\n                             'conflict resolution action should be set to '\n                             '\"NOTHING\".')\n        elif oc._conflict_constraint:\n            raise ValueError('SQLite does not support specifying named '\n                             'constraints for conflict resolution.')\n        elif not oc._conflict_target:\n            raise ValueError('SQLite requires that a conflict target be '\n                             'specified when doing an upsert.')\n\n        return self._build_on_conflict_update(oc, query)\n\n    def extract_date(self, date_part, date_field):\n        return fn.date_part(date_part, date_field, python_value=int)\n\n    def truncate_date(self, date_part, date_field):\n        return fn.date_trunc(date_part, date_field,\n                             python_value=simple_date_time)\n\n    def to_timestamp(self, date_field):\n        return fn.strftime('%s', date_field).cast('integer')\n\n    def from_timestamp(self, date_field):\n        return fn.datetime(date_field, 'unixepoch')\n\n\nclass _BasePsycopgAdapter(object):\n    isolation_levels = {}  # Map int -> str.\n\n    def __init__(self):\n        self.isolation_levels_inv = {\n            v: k for k, v in self.isolation_levels.items()}\n\n    def isolation_level_int(self, isolation_level):\n        if isinstance(isolation_level, str):\n            return self.isolation_levels_inv[isolation_level]\n        return isolation_level\n\n    def isolation_level_str(self, isolation_level):\n        if isinstance(isolation_level, int):\n            return self.isolation_levels[isolation_level]\n        return isolation_level\n\n\nclass Psycopg2Adapter(_BasePsycopgAdapter):\n    isolation_levels = {\n        1: 'READ COMMITTED',\n        2: 'REPEATABLE READ',\n        3: 'SERIALIZABLE',\n        4: 'READ UNCOMMITTED',\n    }\n\n    def __init__(self):\n        super(Psycopg2Adapter, self).__init__()\n        self.json_type = Json_pg2\n        self.jsonb_type = Json_pg2\n        self.cast_json_case = True\n\n    def check_driver(self):\n        if psycopg2 is None:\n            raise ImproperlyConfigured('psycopg2 postgres driver not found.')\n\n    def get_binary_type(self):\n        return psycopg2.Binary\n\n    def connect(self, db, **params):\n        if db.database.startswith('postgresql://'):\n            params.setdefault('dsn', db.database)\n        else:\n            params.setdefault('dbname', db.database)\n\n        conn = psycopg2.connect(**params)\n        if db._register_unicode:\n            pg_extensions.register_type(pg_extensions.UNICODE, conn)\n            pg_extensions.register_type(pg_extensions.UNICODEARRAY, conn)\n        if db._encoding:\n            conn.set_client_encoding(db._encoding)\n        return conn\n\n    def get_server_version(self, conn):\n        return conn.server_version\n\n    def is_connection_usable(self, conn):\n        txn_status = conn.get_transaction_status()\n        return txn_status < pg_extensions.TRANSACTION_STATUS_INERROR\n\n    def is_connection_reusable(self, conn):\n        txn_status = conn.get_transaction_status()\n        # Do not return connection in an error state, as subsequent queries\n        # will all fail. If the status is unknown then we lost the connection\n        # to the server and the connection should not be re-used.\n        if txn_status == pg_extensions.TRANSACTION_STATUS_UNKNOWN:\n            return False\n        elif txn_status == pg_extensions.TRANSACTION_STATUS_INERROR:\n            conn.reset()\n        elif txn_status != pg_extensions.TRANSACTION_STATUS_IDLE:\n            conn.rollback()\n        return True\n\n    def is_connection_closed(self, conn):\n        txn_status = conn.get_transaction_status()\n        if txn_status == pg_extensions.TRANSACTION_STATUS_UNKNOWN:\n            return True\n        elif txn_status != pg_extensions.TRANSACTION_STATUS_IDLE:\n            conn.rollback()\n        return False\n\n\nclass Psycopg3Adapter(_BasePsycopgAdapter):\n    isolation_levels = {\n        1: 'READ UNCOMMITTED',\n        2: 'READ COMMITTED',\n        3: 'REPEATABLE READ',\n        4: 'SERIALIZABLE',\n    }\n\n    def __init__(self):\n        super(Psycopg3Adapter, self).__init__()\n        self.json_type = Json_pg3\n        self.jsonb_type = Jsonb_pg3\n        self.cast_json_case = False\n\n    def check_driver(self):\n        if psycopg is None:\n            raise ImproperlyConfigured('psycopg postgres driver not found.')\n\n    def get_binary_type(self):\n        return psycopg.Binary\n\n    def connect(self, db, **params):\n        if db.database.startswith('postgresql://'):\n            params.setdefault('conninfo', db.database)\n        else:\n            params.setdefault('dbname', db.database)\n        return psycopg.connect(**params)\n\n    def get_server_version(self, conn):\n        return conn.pgconn.server_version\n\n    def is_connection_usable(self, conn):\n        return conn.pgconn.transaction_status < TransactionStatus.INERROR\n\n    def is_connection_reusable(self, conn):\n        txn_status = conn.pgconn.transaction_status\n        # Do not return connection in an error state, as subsequent queries\n        # will all fail. If the status is unknown then we lost the connection\n        # to the server and the connection should not be re-used.\n        if txn_status == TransactionStatus.UNKNOWN:\n            return False\n        elif txn_status == TransactionStatus.INERROR:\n            conn.reset()\n        elif txn_status != TransactionStatus.IDLE:\n            conn.rollback()\n        return True\n\n    def is_connection_closed(self, conn):\n        txn_status = conn.pgconn.transaction_status\n        if txn_status == TransactionStatus.UNKNOWN:\n            return True\n        elif txn_status != TransactionStatus.IDLE:\n            conn.rollback()\n        return False\n\n\nclass PostgresqlDatabase(Database):\n    field_types = {\n        'AUTO': 'SERIAL',\n        'BIGAUTO': 'BIGSERIAL',\n        'BLOB': 'BYTEA',\n        'BOOL': 'BOOLEAN',\n        'DATETIME': 'TIMESTAMP',\n        'DECIMAL': 'NUMERIC',\n        'DOUBLE': 'DOUBLE PRECISION',\n        'UUID': 'UUID',\n        'UUIDB': 'BYTEA'}\n    operations = {'REGEXP': '~', 'IREGEXP': '~*'}\n    param = '%s'\n\n    compound_select_parentheses = CSQ_PARENTHESES_ALWAYS\n    for_update = True\n    nulls_ordering = True\n    returning_clause = True\n    sequences = True\n\n    psycopg2_adapter = Psycopg2Adapter\n    psycopg3_adapter = Psycopg3Adapter\n\n    def init(self, database, register_unicode=True, encoding=None,\n             isolation_level=None, **kwargs):\n        self._register_unicode = register_unicode\n        self._encoding = encoding\n\n        prefer_psycopg3 = kwargs.pop('prefer_psycopg3', False)\n        if psycopg is not None and prefer_psycopg3:\n            self._adapter = self.psycopg3_adapter()\n        else:\n            self._adapter = self.psycopg2_adapter()\n\n        # Accept a string ('READ COMMITTED') or an int constant. Since the\n        # constants vary between psycopg2 & psycopg3 we have to abstract this.\n        self._isolation_level = self._adapter.isolation_level_int(\n            isolation_level)\n\n        super(PostgresqlDatabase, self).init(database, **kwargs)\n\n    def _connect(self):\n        self._adapter.check_driver()\n\n        # Handle connection-strings nicely, since psycopg will accept them,\n        # and they may be easier when lots of parameters are specified.\n        conn = self._adapter.connect(self, **self.connect_params)\n\n        if self._isolation_level:\n            conn.set_isolation_level(self._isolation_level)\n\n        conn.autocommit = True\n        return conn\n\n    def _set_server_version(self, conn):\n        self.server_version = self._adapter.get_server_version(conn)\n\n    def is_connection_usable(self):\n        if self._state.closed:\n            return False\n\n        # Returns True if we are idle, running a command, or in an active\n        # connection. If the connection is in an error state or the connection\n        # is otherwise unusable, return False.\n        return self._adapter.is_connection_usable(self._state.conn)\n\n    def last_insert_id(self, cursor, query_type=None):\n        try:\n            return cursor if query_type != Insert.SIMPLE else cursor[0][0]\n        except (IndexError, KeyError, TypeError):\n            pass\n\n    def rows_affected(self, cursor):\n        try:\n            return cursor.rowcount\n        except AttributeError:\n            return cursor.cursor.rowcount\n\n    def begin(self, isolation_level=None):\n        if self.is_closed():\n            self.connect()\n        if isolation_level:\n            txn_type = self._adapter.isolation_level_str(isolation_level)\n            stmt = 'BEGIN TRANSACTION ISOLATION LEVEL %s' % txn_type\n        else:\n            stmt = 'BEGIN'\n        with __exception_wrapper__:\n            self.cursor().execute(stmt)\n\n    def get_tables(self, schema=None):\n        query = ('SELECT tablename FROM pg_catalog.pg_tables '\n                 'WHERE schemaname = %s ORDER BY tablename')\n        cursor = self.execute_sql(query, (schema or 'public',))\n        return [table for table, in cursor.fetchall()]\n\n    def get_views(self, schema=None):\n        query = ('SELECT viewname, definition FROM pg_catalog.pg_views '\n                 'WHERE schemaname = %s ORDER BY viewname')\n        cursor = self.execute_sql(query, (schema or 'public',))\n        return [ViewMetadata(view_name, sql.strip(' \\t;'))\n                for (view_name, sql) in cursor.fetchall()]\n\n    def get_indexes(self, table, schema=None):\n        query = \"\"\"\n            SELECT\n                i.relname, idxs.indexdef, idx.indisunique,\n                array_to_string(ARRAY(\n                    SELECT pg_get_indexdef(idx.indexrelid, k + 1, TRUE)\n                    FROM generate_subscripts(idx.indkey, 1) AS k\n                    ORDER BY k), ',')\n            FROM pg_catalog.pg_class AS t\n            INNER JOIN pg_catalog.pg_index AS idx ON t.oid = idx.indrelid\n            INNER JOIN pg_catalog.pg_class AS i ON idx.indexrelid = i.oid\n            INNER JOIN pg_catalog.pg_indexes AS idxs ON\n                (idxs.tablename = t.relname AND idxs.indexname = i.relname)\n            WHERE t.relname = %s AND t.relkind = %s AND idxs.schemaname = %s\n            ORDER BY idx.indisunique DESC, i.relname;\"\"\"\n        cursor = self.execute_sql(query, (table, 'r', schema or 'public'))\n        return [IndexMetadata(name, sql.rstrip(' ;'), columns.split(','),\n                              is_unique, table)\n                for name, sql, is_unique, columns in cursor.fetchall()]\n\n    def get_columns(self, table, schema=None):\n        query = \"\"\"\n            SELECT column_name, is_nullable, data_type, column_default\n            FROM information_schema.columns\n            WHERE table_name = %s AND table_schema = %s\n            ORDER BY ordinal_position\"\"\"\n        cursor = self.execute_sql(query, (table, schema or 'public'))\n        pks = set(self.get_primary_keys(table, schema))\n        return [ColumnMetadata(name, dt, null == 'YES', name in pks, table, df)\n                for name, null, dt, df in cursor.fetchall()]\n\n    def get_primary_keys(self, table, schema=None):\n        query = \"\"\"\n            SELECT kc.column_name\n            FROM information_schema.table_constraints AS tc\n            INNER JOIN information_schema.key_column_usage AS kc ON (\n                tc.table_name = kc.table_name AND\n                tc.table_schema = kc.table_schema AND\n                tc.constraint_name = kc.constraint_name)\n            WHERE\n                tc.constraint_type = %s AND\n                tc.table_name = %s AND\n                tc.table_schema = %s\"\"\"\n        ctype = 'PRIMARY KEY'\n        cursor = self.execute_sql(query, (ctype, table, schema or 'public'))\n        return [pk for pk, in cursor.fetchall()]\n\n    def get_foreign_keys(self, table, schema=None):\n        sql = \"\"\"\n            SELECT DISTINCT\n                kcu.column_name, ccu.table_name, ccu.column_name\n            FROM information_schema.table_constraints AS tc\n            JOIN information_schema.key_column_usage AS kcu\n                ON (tc.constraint_name = kcu.constraint_name AND\n                    tc.constraint_schema = kcu.constraint_schema AND\n                    tc.table_name = kcu.table_name AND\n                    tc.table_schema = kcu.table_schema)\n            JOIN information_schema.constraint_column_usage AS ccu\n                ON (ccu.constraint_name = tc.constraint_name AND\n                    ccu.constraint_schema = tc.constraint_schema)\n            WHERE\n                tc.constraint_type = 'FOREIGN KEY' AND\n                tc.table_name = %s AND\n                tc.table_schema = %s\"\"\"\n        cursor = self.execute_sql(sql, (table, schema or 'public'))\n        return [ForeignKeyMetadata(row[0], row[1], row[2], table)\n                for row in cursor.fetchall()]\n\n    def sequence_exists(self, sequence):\n        res = self.execute_sql(\"\"\"\n            SELECT COUNT(*) FROM pg_class, pg_namespace\n            WHERE relkind='S'\n                AND pg_class.relnamespace = pg_namespace.oid\n                AND relname=%s\"\"\", (sequence,))\n        return bool(res.fetchone()[0])\n\n    def get_binary_type(self):\n        return self._adapter.get_binary_type()\n\n    def conflict_statement(self, on_conflict, query):\n        return\n\n    def conflict_update(self, oc, query):\n        action = oc._action.lower() if oc._action else ''\n        if action in ('ignore', 'nothing'):\n            parts = [SQL('ON CONFLICT')]\n            if oc._conflict_target:\n                parts.append(EnclosedNodeList([\n                    Entity(col) if isinstance(col, str) else col\n                    for col in oc._conflict_target]))\n            parts.append(SQL('DO NOTHING'))\n            return NodeList(parts)\n        elif action and action != 'update':\n            raise ValueError('The only supported actions for conflict '\n                             'resolution with Postgresql are \"ignore\" or '\n                             '\"update\".')\n        elif not oc._update and not oc._preserve:\n            raise ValueError('If you are not performing any updates (or '\n                             'preserving any INSERTed values), then the '\n                             'conflict resolution action should be set to '\n                             '\"IGNORE\".')\n        elif not (oc._conflict_target or oc._conflict_constraint):\n            raise ValueError('Postgres requires that a conflict target be '\n                             'specified when doing an upsert.')\n\n        return self._build_on_conflict_update(oc, query)\n\n    def extract_date(self, date_part, date_field):\n        return fn.EXTRACT(NodeList((SQL(date_part), SQL('FROM'), date_field)))\n\n    def truncate_date(self, date_part, date_field):\n        return fn.DATE_TRUNC(date_part, date_field)\n\n    def interval(self, val):\n        return NodeList((SQL('interval'), val))\n\n    def to_timestamp(self, date_field):\n        return self.extract_date('EPOCH', date_field)\n\n    def from_timestamp(self, date_field):\n        # Ironically, here, Postgres means \"to the Postgresql timestamp type\".\n        return fn.to_timestamp(date_field)\n\n    def get_noop_select(self, ctx):\n        return ctx.sql(Select().columns(SQL('0')).where(SQL('false')))\n\n    def set_time_zone(self, timezone):\n        self.execute_sql('set time zone \\'%s\\';' % timezone.replace(\"'\", \"''\"))\n\n    def set_isolation_level(self, isolation_level):\n        self._isolation_level = self._adapter.isolation_level_int(\n            isolation_level)\n\n\nclass MySQLDatabase(Database):\n    field_types = {\n        'AUTO': 'INTEGER AUTO_INCREMENT',\n        'BIGAUTO': 'BIGINT AUTO_INCREMENT',\n        'BOOL': 'BOOL',\n        'DECIMAL': 'NUMERIC',\n        'DOUBLE': 'DOUBLE PRECISION',\n        'FLOAT': 'FLOAT',\n        'UUID': 'VARCHAR(40)',\n        'UUIDB': 'VARBINARY(16)'}\n    operations = {\n        'LIKE': 'LIKE BINARY',\n        'ILIKE': 'LIKE',\n        'REGEXP': 'REGEXP BINARY',\n        'IREGEXP': 'REGEXP',\n        'XOR': 'XOR'}\n    param = '%s'\n    quote = '``'\n\n    compound_select_parentheses = CSQ_PARENTHESES_UNNESTED\n    for_update = True\n    index_using_precedes_table = True\n    limit_max = 2 ** 64 - 1\n    safe_create_index = False\n    safe_drop_index = False\n    sql_mode = 'PIPES_AS_CONCAT'\n\n    def init(self, database, **kwargs):\n        params = {\n            'charset': 'utf8',\n            'sql_mode': self.sql_mode,\n            'use_unicode': True}\n        params.update(kwargs)\n        if 'password' in params and mysql_passwd:\n            params['passwd'] = params.pop('password')\n        super(MySQLDatabase, self).init(database, **params)\n\n    def _connect(self):\n        if mysql is None:\n            raise ImproperlyConfigured('MySQL driver not installed!')\n        conn = mysql.connect(db=self.database, autocommit=True,\n                             **self.connect_params)\n        return conn\n\n    def _set_server_version(self, conn):\n        try:\n            version_raw = conn.server_version\n        except AttributeError:\n            version_raw = conn.get_server_info()\n        self.server_version = self._extract_server_version(version_raw)\n\n    def _extract_server_version(self, version):\n        if isinstance(version, tuple):\n            return version\n        version = version.lower()\n        if 'maria' in version:\n            match_obj = re.search(r'(1\\d\\.\\d+\\.\\d+)', version)\n        else:\n            match_obj = re.search(r'(\\d{1,2}\\.\\d+\\.\\d+)', version)\n        if match_obj is not None:\n            return tuple(int(num) for num in match_obj.groups()[0].split('.'))\n\n        warnings.warn('Unable to determine MySQL version: \"%s\"' % version)\n        return (0, 0, 0)  # Unable to determine version!\n\n    def is_connection_usable(self):\n        if self._state.closed:\n            return False\n\n        conn = self._state.conn\n        if hasattr(conn, 'ping'):\n            if self.server_version[0] >= 8:\n                args = ()\n            else:\n                args = (False,)\n            try:\n                conn.ping(*args)\n            except Exception:\n                return False\n        return True\n\n    def default_values_insert(self, ctx):\n        return ctx.literal('() VALUES ()')\n\n    def begin(self, isolation_level=None):\n        if self.is_closed():\n            self.connect()\n        with __exception_wrapper__:\n            curs = self.cursor()\n            if isolation_level:\n                curs.execute('SET TRANSACTION ISOLATION LEVEL %s' %\n                             isolation_level)\n            curs.execute('BEGIN')\n\n    def get_tables(self, schema=None):\n        query = ('SELECT table_name FROM information_schema.tables '\n                 'WHERE table_schema = DATABASE() AND table_type != %s '\n                 'ORDER BY table_name')\n        return [table for table, in self.execute_sql(query, ('VIEW',))]\n\n    def get_views(self, schema=None):\n        query = ('SELECT table_name, view_definition '\n                 'FROM information_schema.views '\n                 'WHERE table_schema = DATABASE() ORDER BY table_name')\n        cursor = self.execute_sql(query)\n        return [ViewMetadata(*row) for row in cursor.fetchall()]\n\n    def get_indexes(self, table, schema=None):\n        cursor = self.execute_sql('SHOW INDEX FROM `%s`' % table)\n        unique = set()\n        indexes = {}\n        for row in cursor.fetchall():\n            if not row[1]:\n                unique.add(row[2])\n            indexes.setdefault(row[2], [])\n            indexes[row[2]].append(row[4])\n        return [IndexMetadata(name, None, indexes[name], name in unique, table)\n                for name in indexes]\n\n    def get_columns(self, table, schema=None):\n        sql = \"\"\"\n            SELECT column_name, is_nullable, data_type, column_default\n            FROM information_schema.columns\n            WHERE table_name = %s AND table_schema = DATABASE()\n            ORDER BY ordinal_position\"\"\"\n        cursor = self.execute_sql(sql, (table,))\n        pks = set(self.get_primary_keys(table))\n        return [ColumnMetadata(name, dt, null == 'YES', name in pks, table, df)\n                for name, null, dt, df in cursor.fetchall()]\n\n    def get_primary_keys(self, table, schema=None):\n        cursor = self.execute_sql('SHOW INDEX FROM `%s`' % table)\n        return [row[4] for row in\n                filter(lambda row: row[2] == 'PRIMARY', cursor.fetchall())]\n\n    def get_foreign_keys(self, table, schema=None):\n        query = \"\"\"\n            SELECT column_name, referenced_table_name, referenced_column_name\n            FROM information_schema.key_column_usage\n            WHERE table_name = %s\n                AND table_schema = DATABASE()\n                AND referenced_table_name IS NOT NULL\n                AND referenced_column_name IS NOT NULL\"\"\"\n        cursor = self.execute_sql(query, (table,))\n        return [\n            ForeignKeyMetadata(column, dest_table, dest_column, table)\n            for column, dest_table, dest_column in cursor.fetchall()]\n\n    def get_binary_type(self):\n        return mysql.Binary\n\n    def conflict_statement(self, on_conflict, query):\n        if not on_conflict._action: return\n\n        action = on_conflict._action.lower()\n        if action == 'replace':\n            return SQL('REPLACE')\n        elif action == 'ignore':\n            return SQL('INSERT IGNORE')\n        elif action != 'update':\n            raise ValueError('Un-supported action for conflict resolution. '\n                             'MySQL supports REPLACE, IGNORE and UPDATE.')\n\n    def conflict_update(self, on_conflict, query):\n        if on_conflict._where or on_conflict._conflict_target or \\\n           on_conflict._conflict_constraint:\n            raise ValueError('MySQL does not support the specification of '\n                             'where clauses or conflict targets for conflict '\n                             'resolution.')\n\n        updates = []\n        if on_conflict._preserve:\n            # Here we need to determine which function to use, which varies\n            # depending on the MySQL server version. MySQL and MariaDB prior to\n            # 10.3.3 use \"VALUES\", while MariaDB 10.3.3+ use \"VALUE\".\n            version = self.server_version or (0,)\n            if version[0] >= 10 and version >= (10, 3, 3):\n                VALUE_FN = fn.VALUE\n            else:\n                VALUE_FN = fn.VALUES\n\n            for column in on_conflict._preserve:\n                entity = ensure_entity(column)\n                expression = NodeList((\n                    ensure_entity(column),\n                    SQL('='),\n                    VALUE_FN(entity)))\n                updates.append(expression)\n\n        if on_conflict._update:\n            for k, v in on_conflict._update.items():\n                if not isinstance(v, Node):\n                    # Attempt to resolve string field-names to their respective\n                    # field object, to apply data-type conversions.\n                    if isinstance(k, str):\n                        k = getattr(query.table, k)\n                    if isinstance(k, Field):\n                        v = k.to_value(v)\n                    else:\n                        v = Value(v, unpack=False)\n                updates.append(NodeList((ensure_entity(k), SQL('='), v)))\n\n        if updates:\n            return NodeList((SQL('ON DUPLICATE KEY UPDATE'),\n                             CommaNodeList(updates)))\n\n    def extract_date(self, date_part, date_field):\n        return fn.EXTRACT(NodeList((SQL(date_part), SQL('FROM'), date_field)))\n\n    def truncate_date(self, date_part, date_field):\n        return fn.DATE_FORMAT(date_field, __mysql_date_trunc__[date_part],\n                              python_value=simple_date_time)\n\n    def to_timestamp(self, date_field):\n        return fn.UNIX_TIMESTAMP(date_field)\n\n    def from_timestamp(self, date_field):\n        return fn.FROM_UNIXTIME(date_field)\n\n    def random(self):\n        return fn.rand()\n\n    def get_noop_select(self, ctx):\n        return ctx.literal('DO 0')\n\n\n# TRANSACTION CONTROL.\n\n\nclass _manual(object):\n    def __init__(self, db):\n        self.db = db\n\n    def __call__(self, fn):\n        @wraps(fn)\n        def inner(*args, **kwargs):\n            with _manual(self.db):\n                return fn(*args, **kwargs)\n        return inner\n\n    def __enter__(self):\n        top = self.db.top_transaction()\n        if top is not None and not isinstance(top, _manual):\n            raise ValueError('Cannot enter manual commit block while a '\n                             'transaction is active.')\n        self.db.push_transaction(self)\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if self.db.pop_transaction() is not self:\n            raise ValueError('Transaction stack corrupted while exiting '\n                             'manual commit block.')\n\n\nclass _atomic(object):\n    def __init__(self, db, *args, **kwargs):\n        self.db = db\n        self._transaction_args = (args, kwargs)\n\n    def __call__(self, fn):\n        @wraps(fn)\n        def inner(*args, **kwargs):\n            a, k = self._transaction_args\n            with _atomic(self.db, *a, **k):\n                return fn(*args, **kwargs)\n        return inner\n\n    def __enter__(self):\n        if self.db.transaction_depth() == 0:\n            args, kwargs = self._transaction_args\n            self._helper = self.db.transaction(*args, **kwargs)\n        elif isinstance(self.db.top_transaction(), _manual):\n            raise ValueError('Cannot enter atomic commit block while in '\n                             'manual commit mode.')\n        else:\n            self._helper = self.db.savepoint()\n        return self._helper.__enter__()\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        return self._helper.__exit__(exc_type, exc_val, exc_tb)\n\n\nclass _transaction(object):\n    def __init__(self, db, *args, **kwargs):\n        self.db = db\n        self._begin_args = (args, kwargs)\n\n    def __call__(self, fn):\n        @wraps(fn)\n        def inner(*args, **kwargs):\n            a, k = self._begin_args\n            with _transaction(self.db, *a, **k):\n                return fn(*args, **kwargs)\n        return inner\n\n    def _begin(self):\n        args, kwargs = self._begin_args\n        self.db.begin(*args, **kwargs)\n\n    def commit(self, begin=True):\n        self.db.commit()\n        if begin:\n            self._begin()\n\n    def rollback(self, begin=True):\n        self.db.rollback()\n        if begin:\n            self._begin()\n\n    def __enter__(self):\n        if self.db.transaction_depth() == 0:\n            self._begin()\n        self.db.push_transaction(self)\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        depth = self.db.transaction_depth()\n        self.db.pop_transaction()\n        if exc_type and depth == 1:\n            self.rollback(False)\n        elif depth == 1:\n            try:\n                self.commit(False)\n            except Exception:\n                self.rollback(False)\n                raise\n\n\nclass _savepoint(object):\n    def __init__(self, db, sid=None):\n        self.db = db\n        self.sid = sid or 's' + uuid.uuid4().hex\n        self.quoted_sid = self.sid.join(self.db.quote)\n\n    def __call__(self, fn):\n        @wraps(fn)\n        def inner(*args, **kwargs):\n            with _savepoint(self.db):\n                return fn(*args, **kwargs)\n        return inner\n\n    def _begin(self):\n        self.db.execute_sql('SAVEPOINT %s;' % self.quoted_sid)\n\n    def commit(self, begin=True):\n        self.db.execute_sql('RELEASE SAVEPOINT %s;' % self.quoted_sid)\n        if begin: self._begin()\n\n    def rollback(self, begin=True):\n        self.db.execute_sql('ROLLBACK TO SAVEPOINT %s;' % self.quoted_sid)\n        if begin: self._begin()\n\n    def __enter__(self):\n        self._begin()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if exc_type:\n            self.rollback(False)\n        else:\n            try:\n                self.commit(begin=False)\n            except Exception:\n                self.rollback(begin=False)\n                raise\n\n\n# CURSOR REPRESENTATIONS.\n\n\nclass CursorWrapper(object):\n    def __init__(self, cursor):\n        self.cursor = cursor\n        self.count = 0\n        self.index = 0\n        self.initialized = False\n        self.populated = False\n        self.row_cache = []\n\n    def __iter__(self):\n        if self.populated:\n            return iter(self.row_cache)\n        return ResultIterator(self)\n\n    def __getitem__(self, item):\n        if isinstance(item, slice):\n            stop = item.stop\n            if stop is None or stop < 0:\n                self.fill_cache()\n            else:\n                self.fill_cache(stop)\n            return self.row_cache[item]\n        elif isinstance(item, int):\n            self.fill_cache(item if item > 0 else 0)\n            return self.row_cache[item]\n        else:\n            raise ValueError('CursorWrapper only supports integer and slice '\n                             'indexes.')\n\n    def __len__(self):\n        self.fill_cache()\n        return self.count\n\n    def initialize(self):\n        pass\n\n    def iterate(self, cache=True):\n        row = self.cursor.fetchone()\n        if row is None:\n            self.populated = True\n            self.cursor.close()\n            raise StopIteration\n        elif not self.initialized:\n            self.initialize()  # Lazy initialization.\n            self.initialized = True\n        self.count += 1\n        result = self.process_row(row)\n        if cache:\n            self.row_cache.append(result)\n        return result\n\n    def process_row(self, row):\n        return row\n\n    def iterator(self):\n        \"\"\"Efficient one-pass iteration over the result set.\"\"\"\n        while True:\n            try:\n                yield self.iterate(False)\n            except StopIteration:\n                return\n\n    def fill_cache(self, n=0):\n        n = n or float('Inf')\n        if n < 0:\n            raise ValueError('Negative values are not supported.')\n        if self.populated or n <= self.count:\n            # We've already filled the requested rows.\n            return\n\n        iterator = ResultIterator(self)\n        iterator.index = self.count\n        while not self.populated and (n > self.count):\n            try:\n                iterator.next()\n            except StopIteration:\n                break\n\n    def dedupe_columns(self, columns, valid_identifiers=True):\n        # Try to clean-up messy column descriptions when people do not\n        # provide an alias. The idea is that we take something like:\n        # SUM(\"t1\".\"price\") -> \"price\") -> price. Similarly, duplicated column\n        # names will get an integer suffix, e.g. val, val_2, val_3.\n        identifiers = []\n        duplicates = {}\n        for column in columns:\n            if valid_identifiers:\n                column = make_identifier(column)\n\n            if column in duplicates:\n                duplicates[column] += 1\n                column = '%s_%s' % (column, duplicates[column])\n            else:\n                duplicates[column] = 1\n            identifiers.append(column)\n        return identifiers\n\n\nclass DictCursorWrapper(CursorWrapper):\n    def initialize(self):\n        self.columns = self.dedupe_columns(\n            [col_spec[0] for col_spec in self.cursor.description],\n            valid_identifiers=False)\n        self.ncols = len(self.columns)\n\n    def _row_to_dict(self, row):\n        result = {}\n        for i in range(self.ncols):\n            result.setdefault(self.columns[i], row[i])  # Do not overwrite.\n        return result\n\n    process_row = _row_to_dict\n\nclass NamedTupleCursorWrapper(CursorWrapper):\n    def initialize(self):\n        identifiers = self.dedupe_columns(\n            [col_spec[0] for col_spec in self.cursor.description])\n        self.tuple_class = collections.namedtuple('Row', identifiers)\n\n    def process_row(self, row):\n        return self.tuple_class(*row)\n\n\nclass ObjectCursorWrapper(DictCursorWrapper):\n    def __init__(self, cursor, constructor):\n        super(ObjectCursorWrapper, self).__init__(cursor)\n        self.constructor = constructor\n\n    def initialize(self):\n        self.columns = self.dedupe_columns(\n            [col_spec[0] for col_spec in self.cursor.description],\n            valid_identifiers=True)\n        self.ncols = len(self.columns)\n\n    def process_row(self, row):\n        row_dict = self._row_to_dict(row)\n        return self.constructor(**row_dict)\n\n\nclass ResultIterator(object):\n    def __init__(self, cursor_wrapper):\n        self.cursor_wrapper = cursor_wrapper\n        self.index = 0\n\n    def __iter__(self):\n        return self\n\n    def next(self):\n        if self.index < self.cursor_wrapper.count:\n            obj = self.cursor_wrapper.row_cache[self.index]\n        elif not self.cursor_wrapper.populated:\n            self.cursor_wrapper.iterate()\n            obj = self.cursor_wrapper.row_cache[self.index]\n        else:\n            raise StopIteration\n        self.index += 1\n        return obj\n\n    __next__ = next\n\n# FIELDS\n\nclass FieldAccessor(object):\n    def __init__(self, model, field, name):\n        self.model = model\n        self.field = field\n        self.name = name\n\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            try:\n                return instance.__data__[self.name]\n            except KeyError:\n                return\n        return self.field\n\n    def __set__(self, instance, value):\n        instance.__data__[self.name] = value\n        instance._dirty.add(self.name)\n\n\nclass ForeignKeyAccessor(FieldAccessor):\n    def __init__(self, model, field, name):\n        super(ForeignKeyAccessor, self).__init__(model, field, name)\n        self.rel_model = field.rel_model\n\n    def get_rel_instance(self, instance):\n        value = instance.__data__.get(self.name)\n        if value is not None or self.name in instance.__rel__:\n            if self.name not in instance.__rel__ and self.field.lazy_load:\n                obj = self.rel_model.get(self.field.rel_field == value)\n                instance.__rel__[self.name] = obj\n            return instance.__rel__.get(self.name, value)\n        elif not self.field.null and self.field.lazy_load:\n            raise self.rel_model.DoesNotExist\n        return value\n\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            return self.get_rel_instance(instance)\n        return self.field\n\n    def __set__(self, instance, obj):\n        if isinstance(obj, self.rel_model):\n            instance.__data__[self.name] = getattr(obj, self.field.rel_field.name)\n            instance.__rel__[self.name] = obj\n        else:\n            fk_value = instance.__data__.get(self.name)\n            instance.__data__[self.name] = obj\n            if (obj != fk_value or obj is None) and \\\n               self.name in instance.__rel__:\n                del instance.__rel__[self.name]\n        instance._dirty.add(self.name)\n\n\nclass BackrefAccessor(object):\n    def __init__(self, field):\n        self.field = field\n        self.model = field.rel_model\n        self.rel_model = field.model\n\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            dest = self.field.rel_field.name\n            return (self.rel_model\n                    .select()\n                    .where(self.field == getattr(instance, dest)))\n        return self\n\n\nclass ObjectIdAccessor(object):\n    \"\"\"Gives direct access to the underlying id\"\"\"\n    def __init__(self, field):\n        self.field = field\n\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            value = instance.__data__.get(self.field.name)\n            # Pull the object-id from the related object if it is not set.\n            if value is None and self.field.name in instance.__rel__:\n                rel_obj = instance.__rel__[self.field.name]\n                value = getattr(rel_obj, self.field.rel_field.name)\n            return value\n        return self.field\n\n    def __set__(self, instance, value):\n        setattr(instance, self.field.name, value)\n\n\nclass Field(ColumnBase):\n    _field_counter = 0\n    _order = 0\n    accessor_class = FieldAccessor\n    auto_increment = False\n    default_index_type = None\n    field_type = 'DEFAULT'\n    unpack = True\n\n    def __init__(self, null=False, index=False, unique=False, column_name=None,\n                 default=None, primary_key=False, constraints=None,\n                 sequence=None, collation=None, unindexed=False, choices=None,\n                 help_text=None, verbose_name=None, index_type=None,\n                 db_column=None, _hidden=False):\n        if db_column is not None:\n            __deprecated__('\"db_column\" has been deprecated in favor of '\n                           '\"column_name\" for Field objects.')\n            column_name = db_column\n\n        self.null = null\n        self.index = index\n        self.unique = unique\n        self.column_name = column_name\n        self.default = default\n        self.primary_key = primary_key\n        self.constraints = constraints  # List of column constraints.\n        self.sequence = sequence  # Name of sequence, e.g. foo_id_seq.\n        self.collation = collation\n        self.unindexed = unindexed\n        self.choices = choices\n        self.help_text = help_text\n        self.verbose_name = verbose_name\n        self.index_type = index_type or self.default_index_type\n        self._hidden = _hidden\n\n        # Used internally for recovering the order in which Fields were defined\n        # on the Model class.\n        Field._field_counter += 1\n        self._order = Field._field_counter\n        self._sort_key = (self.primary_key and 1 or 2), self._order\n\n    def __hash__(self):\n        return hash(self.name + '.' + self.model.__name__)\n\n    def __repr__(self):\n        if hasattr(self, 'model') and getattr(self, 'name', None):\n            return '<%s: %s.%s>' % (type(self).__name__,\n                                    self.model.__name__,\n                                    self.name)\n        return '<%s: (unbound)>' % type(self).__name__\n\n    def bind(self, model, name, set_attribute=True):\n        self.model = model\n        self.name = self.safe_name = name\n        self.column_name = self.column_name or name\n        if set_attribute:\n            setattr(model, name, self.accessor_class(model, self, name))\n\n    @property\n    def column(self):\n        return Column(self.model._meta.table, self.column_name)\n\n    def adapt(self, value):\n        return value\n\n    def db_value(self, value):\n        return value if value is None else self.adapt(value)\n\n    def python_value(self, value):\n        return value if value is None else self.adapt(value)\n\n    def to_value(self, value, case=False):\n        return Value(value, self.db_value, unpack=False)\n\n    def get_sort_key(self, ctx):\n        return self._sort_key\n\n    def __sql__(self, ctx):\n        return ctx.sql(self.column)\n\n    def get_modifiers(self):\n        pass\n\n    def ddl_datatype(self, ctx):\n        if ctx and ctx.state.field_types:\n            column_type = ctx.state.field_types.get(self.field_type,\n                                                    self.field_type)\n        else:\n            column_type = self.field_type\n\n        modifiers = self.get_modifiers()\n        if column_type and modifiers:\n            modifier_literal = ', '.join([str(m) for m in modifiers])\n            return SQL('%s(%s)' % (column_type, modifier_literal))\n        else:\n            return SQL(column_type)\n\n    def ddl(self, ctx):\n        accum = [Entity(self.column_name)]\n        data_type = self.ddl_datatype(ctx)\n        if data_type:\n            accum.append(data_type)\n        if self.unindexed:\n            accum.append(SQL('UNINDEXED'))\n        if not self.null:\n            accum.append(SQL('NOT NULL'))\n        if self.primary_key:\n            accum.append(SQL('PRIMARY KEY'))\n        if self.sequence:\n            accum.append(SQL(\"DEFAULT NEXTVAL('%s')\" % self.sequence))\n        if self.constraints:\n            accum.extend(self.constraints)\n        if self.collation:\n            accum.append(SQL('COLLATE %s' % self.collation))\n        return NodeList(accum)\n\n\nclass AnyField(Field):\n    field_type = 'ANY'\n\n\nclass IntegerField(Field):\n    field_type = 'INT'\n\n    def adapt(self, value):\n        try:\n            return int(value)\n        except (ValueError, TypeError):\n            return value\n\n\nclass BigIntegerField(IntegerField):\n    field_type = 'BIGINT'\n\n\nclass SmallIntegerField(IntegerField):\n    field_type = 'SMALLINT'\n\n\nclass AutoField(IntegerField):\n    auto_increment = True\n    field_type = 'AUTO'\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get('primary_key') is False:\n            raise ValueError('%s must always be a primary key.' % type(self))\n        kwargs['primary_key'] = True\n        super(AutoField, self).__init__(*args, **kwargs)\n\n\nclass BigAutoField(AutoField):\n    field_type = 'BIGAUTO'\n\n\nclass IdentityField(AutoField):\n    field_type = 'INT GENERATED BY DEFAULT AS IDENTITY'\n\n    def __init__(self, generate_always=False, **kwargs):\n        if generate_always:\n            self.field_type = 'INT GENERATED ALWAYS AS IDENTITY'\n        super(IdentityField, self).__init__(**kwargs)\n\n\nclass PrimaryKeyField(AutoField):\n    def __init__(self, *args, **kwargs):\n        __deprecated__('\"PrimaryKeyField\" has been renamed to \"AutoField\". '\n                       'Please update your code accordingly as this will be '\n                       'completely removed in a subsequent release.')\n        super(PrimaryKeyField, self).__init__(*args, **kwargs)\n\n\nclass FloatField(Field):\n    field_type = 'FLOAT'\n\n    def adapt(self, value):\n        try:\n            return float(value)\n        except (ValueError, TypeError):\n            return value\n\n\nclass DoubleField(FloatField):\n    field_type = 'DOUBLE'\n\n\nclass DecimalField(Field):\n    field_type = 'DECIMAL'\n\n    def __init__(self, max_digits=10, decimal_places=5, auto_round=False,\n                 rounding=None, *args, **kwargs):\n        self.max_digits = max_digits\n        self.decimal_places = decimal_places\n        self.auto_round = auto_round\n        self.rounding = rounding or decimal.DefaultContext.rounding\n        self._exp = decimal.Decimal(10) ** (-self.decimal_places)\n        super(DecimalField, self).__init__(*args, **kwargs)\n\n    def get_modifiers(self):\n        return [self.max_digits, self.decimal_places]\n\n    def db_value(self, value):\n        D = decimal.Decimal\n        if not value:\n            return value if value is None else D(0)\n        if self.auto_round:\n            decimal_value = D(str(value))\n            return decimal_value.quantize(self._exp, rounding=self.rounding)\n        return value\n\n    def python_value(self, value):\n        if value is not None:\n            if isinstance(value, decimal.Decimal):\n                return value\n            return decimal.Decimal(str(value))\n\n\nclass _StringField(Field):\n    def adapt(self, value):\n        if isinstance(value, str):\n            return value\n        elif isinstance(value, bytes):\n            return value.decode('utf-8')\n        return str(value)\n\n    def __add__(self, other): return StringExpression(self, OP.CONCAT, other)\n    def __radd__(self, other): return StringExpression(other, OP.CONCAT, self)\n\n\nclass CharField(_StringField):\n    field_type = 'VARCHAR'\n\n    def __init__(self, max_length=255, *args, **kwargs):\n        self.max_length = max_length\n        super(CharField, self).__init__(*args, **kwargs)\n\n    def get_modifiers(self):\n        return self.max_length and [self.max_length] or None\n\n\nclass FixedCharField(CharField):\n    field_type = 'CHAR'\n\n    def adapt(self, value):\n        value = super(FixedCharField, self).adapt(value)\n        if value:\n            value = value[:self.max_length]\n        return value\n\n\nclass TextField(_StringField):\n    field_type = 'TEXT'\n\n\nclass FieldDatabaseHook(object):\n    def _db_hook(self, database):\n        raise NotImplementedError('Subclasses must implement')\n\n    def bind(self, model, name, set_attribute=True):\n        if model._meta.database is not None:\n            if isinstance(model._meta.database, Proxy):\n                model._meta.database.attach_callback(self._db_hook)\n                self._db_hook(None)\n            else:\n                self._db_hook(model._meta.database)\n        else:\n            self._db_hook(None)\n\n        # Attach a hook to the model metadata; in the event the database is\n        # changed or set at run-time, we will be sure to apply our callback and\n        # use the proper data-type for our database driver.\n        model._meta._db_hooks.append(self._db_hook)\n        return super(FieldDatabaseHook, self).bind(model, name, set_attribute)\n\n\nclass BlobField(FieldDatabaseHook, Field):\n    field_type = 'BLOB'\n\n    def _db_hook(self, database):\n        if database is None:\n            self._constructor = bytearray\n        else:\n            self._constructor = database.get_binary_type()\n\n    def db_value(self, value):\n        if isinstance(value, str):\n            value = value.encode('raw_unicode_escape')\n        if isinstance(value, bytes):\n            return self._constructor(value)\n        return value\n\n\nclass BitField(BitwiseMixin, BigIntegerField):\n    def __init__(self, *args, **kwargs):\n        kwargs.setdefault('default', 0)\n        super(BitField, self).__init__(*args, **kwargs)\n        self.__current_flag = 1\n\n    def flag(self, value=None):\n        if value is None:\n            value = self.__current_flag\n            self.__current_flag <<= 1\n        else:\n            self.__current_flag = value << 1\n\n        class FlagDescriptor(ColumnBase):\n            def __init__(self, field, value):\n                self._field = field\n                self._value = value\n                super(FlagDescriptor, self).__init__()\n            def clear(self):\n                return self._field.bin_and(~self._value)\n            def set(self):\n                return self._field.bin_or(self._value)\n            def __get__(self, instance, instance_type=None):\n                if instance is None:\n                    return self\n                value = getattr(instance, self._field.name) or 0\n                return (value & self._value) != 0\n            def __set__(self, instance, is_set):\n                if is_set not in (True, False):\n                    raise ValueError('Value must be either True or False')\n                value = getattr(instance, self._field.name) or 0\n                if is_set:\n                    value |= self._value\n                else:\n                    value &= ~self._value\n                setattr(instance, self._field.name, value)\n            def __sql__(self, ctx):\n                return ctx.sql(self._field.bin_and(self._value) != 0)\n        return FlagDescriptor(self, value)\n\n\nclass BigBitFieldData(object):\n    def __init__(self, instance, name):\n        self.instance = instance\n        self.name = name\n        value = self.instance.__data__.get(self.name)\n        if not value:\n            value = bytearray()\n        elif not isinstance(value, bytearray):\n            value = bytearray(value)\n        self._buffer = self.instance.__data__[self.name] = value\n\n    def clear(self):\n        self._buffer.clear()\n\n    def _ensure_length(self, idx):\n        byte_num, byte_offset = divmod(idx, 8)\n        cur_size = len(self._buffer)\n        if cur_size <= byte_num:\n            self._buffer.extend(b'\\x00' * ((byte_num + 1) - cur_size))\n        return byte_num, byte_offset\n\n    def set_bit(self, idx):\n        byte_num, byte_offset = self._ensure_length(idx)\n        self._buffer[byte_num] |= (1 << byte_offset)\n\n    def clear_bit(self, idx):\n        byte_num, byte_offset = self._ensure_length(idx)\n        self._buffer[byte_num] &= ~(1 << byte_offset)\n\n    def toggle_bit(self, idx):\n        byte_num, byte_offset = self._ensure_length(idx)\n        self._buffer[byte_num] ^= (1 << byte_offset)\n        return bool(self._buffer[byte_num] & (1 << byte_offset))\n\n    def is_set(self, idx):\n        byte_num, byte_offset = divmod(idx, 8)\n        cur_size = len(self._buffer)\n        if cur_size <= byte_num:\n            return False\n        return bool(self._buffer[byte_num] & (1 << byte_offset))\n\n    __getitem__ = is_set\n    def __setitem__(self, item, value):\n        self.set_bit(item) if value else self.clear_bit(item)\n    __delitem__ = clear_bit\n\n    def __len__(self):\n        return len(self._buffer)\n\n    def _get_compatible_data(self, other):\n        if isinstance(other, BigBitFieldData):\n            data = other._buffer\n        elif isinstance(other, (bytes, bytearray, memoryview)):\n            data = other\n        else:\n            raise ValueError('Incompatible data-type')\n        diff = len(data) - len(self)\n        if diff > 0: self._buffer.extend(b'\\x00' * diff)\n        return data\n\n    def _bitwise_op(self, other, op):\n        if isinstance(other, BigBitFieldData):\n            data = other._buffer\n        elif isinstance(other, (bytes, bytearray, memoryview)):\n            data = other\n        else:\n            raise ValueError('Incompatible data-type')\n        buf = bytearray(b'\\x00' * max(len(self), len(other)))\n        it = itertools.zip_longest(self._buffer, data, fillvalue=0)\n        for i, (a, b) in enumerate(it):\n            buf[i] = op(a, b)\n        return buf\n\n    def __and__(self, other):\n        return self._bitwise_op(other, operator.and_)\n    def __or__(self, other):\n        return self._bitwise_op(other, operator.or_)\n    def __xor__(self, other):\n        return self._bitwise_op(other, operator.xor)\n\n    def __iter__(self):\n        for b in self._buffer:\n            for j in range(8):\n                yield 1 if (b & (1 << j)) else 0\n\n    def __repr__(self):\n        return repr(self._buffer)\n\n    def __bytes__(self):\n        return bytes(self._buffer)\n\n\nclass BigBitFieldAccessor(FieldAccessor):\n    def __get__(self, instance, instance_type=None):\n        if instance is None:\n            return self.field\n        return BigBitFieldData(instance, self.name)\n    def __set__(self, instance, value):\n        if isinstance(value, memoryview):\n            value = value.tobytes()\n        elif isinstance(value, bytearray):\n            value = bytes(value)\n        elif isinstance(value, BigBitFieldData):\n            value = bytes(value._buffer)\n        elif isinstance(value, str):\n            value = value.encode('utf-8')\n        elif not isinstance(value, bytes):\n            raise ValueError('Value must be either a bytes, memoryview or '\n                             'BigBitFieldData instance.')\n        super(BigBitFieldAccessor, self).__set__(instance, value)\n\n\nclass BigBitField(BlobField):\n    accessor_class = BigBitFieldAccessor\n\n    def __init__(self, *args, **kwargs):\n        kwargs.setdefault('default', bytes)\n        super(BigBitField, self).__init__(*args, **kwargs)\n\n    def db_value(self, value):\n        return bytes(value) if value is not None else value\n\n\nclass UUIDField(Field):\n    field_type = 'UUID'\n\n    def db_value(self, value):\n        if isinstance(value, str) and len(value) == 32:\n            # Hex string. No transformation is necessary.\n            return value\n        elif isinstance(value, bytes) and len(value) == 16:\n            # Allow raw binary representation.\n            value = uuid.UUID(bytes=value)\n        if isinstance(value, uuid.UUID):\n            return value.hex\n        try:\n            return uuid.UUID(value).hex\n        except Exception:\n            return value\n\n    def python_value(self, value):\n        if isinstance(value, uuid.UUID):\n            return value\n        return uuid.UUID(value) if value is not None else None\n\n\nclass BinaryUUIDField(BlobField):\n    field_type = 'UUIDB'\n\n    def db_value(self, value):\n        if isinstance(value, bytes) and len(value) == 16:\n            # Raw binary value. No transformation is necessary.\n            return self._constructor(value)\n        elif isinstance(value, str) and len(value) == 32:\n            # Allow hex string representation.\n            value = uuid.UUID(hex=value)\n        if isinstance(value, uuid.UUID):\n            return self._constructor(value.bytes)\n        elif value is not None:\n            raise ValueError('value for binary UUID field must be UUID(), '\n                             'a hexadecimal string, or a bytes object.')\n\n    def python_value(self, value):\n        if isinstance(value, uuid.UUID):\n            return value\n        elif isinstance(value, memoryview):\n            value = value.tobytes()\n        elif value and not isinstance(value, bytes):\n            value = bytes(value)\n        return uuid.UUID(bytes=value) if value is not None else None\n\n\ndef _date_part(date_part):\n    def dec(self):\n        return self.model._meta.database.extract_date(date_part, self)\n    return dec\n\ndef format_date_time(value, formats, post_process=None):\n    post_process = post_process or (lambda x: x)\n    for fmt in formats:\n        try:\n            return post_process(datetime.datetime.strptime(value, fmt))\n        except ValueError:\n            pass\n    return value\n\ndef simple_date_time(value):\n    try:\n        return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S')\n    except (TypeError, ValueError):\n        return value\n\n\nclass _BaseFormattedField(Field):\n    formats = None\n\n    def __init__(self, formats=None, *args, **kwargs):\n        if formats is not None:\n            self.formats = formats\n        super(_BaseFormattedField, self).__init__(*args, **kwargs)\n\n\nclass DateTimeField(_BaseFormattedField):\n    field_type = 'DATETIME'\n    formats = [\n        '%Y-%m-%d %H:%M:%S.%f',\n        '%Y-%m-%d %H:%M:%S',\n        '%Y-%m-%d %H:%M:%S.%f%z',\n        '%Y-%m-%d %H:%M:%S%z',\n        '%Y-%m-%d',\n    ]\n\n    def adapt(self, value):\n        if value and isinstance(value, str):\n            return format_date_time(value, self.formats)\n        return value\n\n    def to_timestamp(self):\n        return self.model._meta.database.to_timestamp(self)\n\n    def truncate(self, part):\n        return self.model._meta.database.truncate_date(part, self)\n\n    year = property(_date_part('year'))\n    month = property(_date_part('month'))\n    day = property(_date_part('day'))\n    hour = property(_date_part('hour'))\n    minute = property(_date_part('minute'))\n    second = property(_date_part('second'))\n\n\nclass DateField(_BaseFormattedField):\n    field_type = 'DATE'\n    formats = [\n        '%Y-%m-%d',\n        '%Y-%m-%d %H:%M:%S',\n        '%Y-%m-%d %H:%M:%S.%f',\n    ]\n\n    def adapt(self, value):\n        if value and isinstance(value, str):\n            pp = lambda x: x.date()\n            return format_date_time(value, self.formats, pp)\n        elif value and isinstance(value, datetime.datetime):\n            return value.date()\n        return value\n\n    def to_timestamp(self):\n        return self.model._meta.database.to_timestamp(self)\n\n    def truncate(self, part):\n        return self.model._meta.database.truncate_date(part, self)\n\n    year = property(_date_part('year'))\n    month = property(_date_part('month'))\n    day = property(_date_part('day'))\n\n\nclass TimeField(_BaseFormattedField):\n    field_type = 'TIME'\n    formats = [\n        '%H:%M:%S.%f',\n        '%H:%M:%S',\n        '%H:%M',\n        '%Y-%m-%d %H:%M:%S.%f',\n        '%Y-%m-%d %H:%M:%S',\n    ]\n\n    def adapt(self, value):\n        if value:\n            if isinstance(value, str):\n                pp = lambda x: x.time()\n                return format_date_time(value, self.formats, pp)\n            elif isinstance(value, datetime.datetime):\n                return value.time()\n        if value is not None and isinstance(value, datetime.timedelta):\n            return (datetime.datetime.min + value).time()\n        return value\n\n    hour = property(_date_part('hour'))\n    minute = property(_date_part('minute'))\n    second = property(_date_part('second'))\n\n\ndef _timestamp_date_part(date_part):\n    def dec(self):\n        db = self.model._meta.database\n        expr = ((self / Value(self.resolution, converter=False))\n                if self.resolution > 1 else self)\n        return db.extract_date(date_part, db.from_timestamp(expr))\n    return dec\n\n\nclass TimestampField(BigIntegerField):\n    # Support second -> microsecond resolution.\n    valid_resolutions = [10**i for i in range(7)]\n\n    def __init__(self, *args, **kwargs):\n        self.resolution = kwargs.pop('resolution', None)\n\n        if not self.resolution:\n            self.resolution = 1\n        elif self.resolution in range(2, 7):\n            self.resolution = 10 ** self.resolution\n        elif self.resolution not in self.valid_resolutions:\n            raise ValueError('TimestampField resolution must be one of: %s' %\n                             ', '.join(str(i) for i in self.valid_resolutions))\n        self.ticks_to_microsecond = 1000000 // self.resolution\n\n        self.utc = kwargs.pop('utc', False) or False\n        dflt = utcnow if self.utc else datetime.datetime.now\n        kwargs.setdefault('default', dflt)\n        super(TimestampField, self).__init__(*args, **kwargs)\n\n    def local_to_utc(self, dt):\n        # Convert naive local datetime into naive UTC, e.g.:\n        # 2019-03-01T12:00:00 (local=US/Central) -> 2019-03-01T18:00:00.\n        # 2019-05-01T12:00:00 (local=US/Central) -> 2019-05-01T17:00:00.\n        # 2019-03-01T12:00:00 (local=UTC)        -> 2019-03-01T12:00:00.\n        return datetime.datetime(*time.gmtime(time.mktime(dt.timetuple()))[:6])\n\n    def utc_to_local(self, dt):\n        # Convert a naive UTC datetime into local time, e.g.:\n        # 2019-03-01T18:00:00 (local=US/Central) -> 2019-03-01T12:00:00.\n        # 2019-05-01T17:00:00 (local=US/Central) -> 2019-05-01T12:00:00.\n        # 2019-03-01T12:00:00 (local=UTC)        -> 2019-03-01T12:00:00.\n        ts = calendar.timegm(dt.utctimetuple())\n        return datetime.datetime.fromtimestamp(ts)\n\n    def get_timestamp(self, value):\n        if self.utc:\n            # If utc-mode is on, then we assume all naive datetimes are in UTC.\n            return calendar.timegm(value.utctimetuple())\n        else:\n            return time.mktime(value.timetuple())\n\n    def db_value(self, value):\n        if value is None:\n            return\n\n        if isinstance(value, datetime.datetime):\n            pass\n        elif isinstance(value, datetime.date):\n            value = datetime.datetime(value.year, value.month, value.day)\n        else:\n            return int(round(value * self.resolution))\n\n        timestamp = self.get_timestamp(value)\n        if self.resolution > 1:\n            timestamp += (value.microsecond * .000001)\n            timestamp *= self.resolution\n        return int(round(timestamp))\n\n    def python_value(self, value):\n        if value is not None and isinstance(value, (int, float)):\n            if self.resolution > 1:\n                value, ticks = divmod(value, self.resolution)\n                microseconds = int(ticks * self.ticks_to_microsecond)\n            else:\n                microseconds = 0\n\n            if self.utc:\n                value = utcfromtimestamp(value)\n            else:\n                value = datetime.datetime.fromtimestamp(value)\n\n            if microseconds:\n                value = value.replace(microsecond=microseconds)\n\n        return value\n\n    def from_timestamp(self):\n        expr = ((self / Value(self.resolution, converter=False))\n                if self.resolution > 1 else self)\n        return self.model._meta.database.from_timestamp(expr)\n\n    year = property(_timestamp_date_part('year'))\n    month = property(_timestamp_date_part('month'))\n    day = property(_timestamp_date_part('day'))\n    hour = property(_timestamp_date_part('hour'))\n    minute = property(_timestamp_date_part('minute'))\n    second = property(_timestamp_date_part('second'))\n\n\nclass IPField(BigIntegerField):\n    def db_value(self, val):\n        if val is not None:\n            return struct.unpack('!I', socket.inet_aton(val))[0]\n\n    def python_value(self, val):\n        if val is not None:\n            return socket.inet_ntoa(struct.pack('!I', val))\n\n\nclass BooleanField(Field):\n    field_type = 'BOOL'\n    adapt = bool\n\n\nclass BareField(Field):\n    def __init__(self, adapt=None, *args, **kwargs):\n        super(BareField, self).__init__(*args, **kwargs)\n        if adapt is not None:\n            self.adapt = adapt\n\n    def ddl_datatype(self, ctx):\n        return\n\n\nclass ForeignKeyField(Field):\n    accessor_class = ForeignKeyAccessor\n    backref_accessor_class = BackrefAccessor\n\n    def __init__(self, model, field=None, backref=None, on_delete=None,\n                 on_update=None, deferrable=None, _deferred=None,\n                 rel_model=None, to_field=None, object_id_name=None,\n                 lazy_load=True, constraint_name=None, related_name=None,\n                 *args, **kwargs):\n        kwargs.setdefault('index', True)\n\n        super(ForeignKeyField, self).__init__(*args, **kwargs)\n\n        if rel_model is not None:\n            __deprecated__('\"rel_model\" has been deprecated in favor of '\n                           '\"model\" for ForeignKeyField objects.')\n            model = rel_model\n        if to_field is not None:\n            __deprecated__('\"to_field\" has been deprecated in favor of '\n                           '\"field\" for ForeignKeyField objects.')\n            field = to_field\n        if related_name is not None:\n            __deprecated__('\"related_name\" has been deprecated in favor of '\n                           '\"backref\" for Field objects.')\n            backref = related_name\n\n        self._is_self_reference = model == 'self'\n        self.rel_model = model\n        self.rel_field = field\n        self.declared_backref = backref\n        self.backref = None\n        self.on_delete = on_delete\n        self.on_update = on_update\n        self.deferrable = deferrable\n        self.deferred = _deferred\n        self.object_id_name = object_id_name\n        self.lazy_load = lazy_load\n        self.constraint_name = constraint_name\n\n    @property\n    def field_type(self):\n        if isinstance(self.rel_field, BigAutoField):\n            return BigIntegerField.field_type\n        elif not isinstance(self.rel_field, AutoField):\n            return self.rel_field.field_type\n        return IntegerField.field_type\n\n    def get_modifiers(self):\n        if not isinstance(self.rel_field, AutoField):\n            return self.rel_field.get_modifiers()\n        return super(ForeignKeyField, self).get_modifiers()\n\n    def get_constraint_name(self):\n        return self.constraint_name or 'fk_%s_%s_refs_%s' % (\n            self.model._meta.table_name,\n            self.column_name,\n            self.rel_model._meta.table_name)\n\n    def adapt(self, value):\n        return self.rel_field.adapt(value)\n\n    def db_value(self, value):\n        if isinstance(value, self.rel_model):\n            value = getattr(value, self.rel_field.name)\n        return self.rel_field.db_value(value)\n\n    def python_value(self, value):\n        if isinstance(value, self.rel_model):\n            return value\n        return self.rel_field.python_value(value)\n\n    def bind(self, model, name, set_attribute=True):\n        if not self.column_name:\n            self.column_name = name if name.endswith('_id') else name + '_id'\n        if not self.object_id_name:\n            self.object_id_name = self.column_name\n            if self.object_id_name == name:\n                self.object_id_name += '_id'\n        elif self.object_id_name == name:\n            raise ValueError('ForeignKeyField \"%s\".\"%s\" specifies an '\n                             'object_id_name that conflicts with its field '\n                             'name.' % (model._meta.name, name))\n        if self._is_self_reference:\n            self.rel_model = model\n        if isinstance(self.rel_field, str):\n            self.rel_field = getattr(self.rel_model, self.rel_field)\n        elif self.rel_field is None:\n            self.rel_field = self.rel_model._meta.primary_key\n\n        # Bind field before assigning backref, so field is bound when\n        # calling declared_backref() (if callable).\n        super(ForeignKeyField, self).bind(model, name, set_attribute)\n        self.safe_name = self.object_id_name\n\n        if callable_(self.declared_backref):\n            self.backref = self.declared_backref(self)\n        else:\n            self.backref, self.declared_backref = self.declared_backref, None\n        if not self.backref:\n            self.backref = '%s_set' % model._meta.name\n\n        if set_attribute:\n            setattr(model, self.object_id_name, ObjectIdAccessor(self))\n            if self.backref not in '!+':\n                setattr(self.rel_model, self.backref,\n                        self.backref_accessor_class(self))\n\n    def foreign_key_constraint(self, explicit_name=False):\n        parts = []\n        if self.constraint_name or explicit_name:\n            name = self.get_constraint_name()\n            parts.extend([\n                SQL('CONSTRAINT'),\n                Entity(_truncate_constraint_name(name))])\n\n        parts.extend([\n            SQL('FOREIGN KEY'),\n            EnclosedNodeList((self,)),\n            SQL('REFERENCES'),\n            self.rel_model,\n            EnclosedNodeList((self.rel_field,))])\n        if self.on_delete:\n            parts.append(SQL('ON DELETE %s' % self.on_delete))\n        if self.on_update:\n            parts.append(SQL('ON UPDATE %s' % self.on_update))\n        if self.deferrable:\n            parts.append(SQL('DEFERRABLE %s' % self.deferrable))\n        return NodeList(parts)\n\n    def __getattr__(self, attr):\n        if attr.startswith('__'):\n            # Prevent recursion error when deep-copying.\n            raise AttributeError('Cannot look-up non-existant \"__\" methods.')\n        if attr in self.rel_model._meta.fields:\n            return self.rel_model._meta.fields[attr]\n        raise AttributeError('Foreign-key has no attribute %s, nor is it a '\n                             'valid field on the related model.' % attr)\n\n\nclass DeferredForeignKey(Field):\n    _unresolved = set()\n\n    def __init__(self, rel_model_name, **kwargs):\n        self.field_kwargs = kwargs\n        self.rel_model_name = rel_model_name.lower()\n        DeferredForeignKey._unresolved.add(self)\n        super(DeferredForeignKey, self).__init__(\n            column_name=kwargs.get('column_name'),\n            null=kwargs.get('null'),\n            primary_key=kwargs.get('primary_key'))\n\n    __hash__ = object.__hash__\n\n    def __deepcopy__(self, memo=None):\n        return DeferredForeignKey(self.rel_model_name, **self.field_kwargs)\n\n    def set_model(self, rel_model):\n        field = ForeignKeyField(rel_model, _deferred=True, **self.field_kwargs)\n        if field.primary_key:\n            # NOTE: this calls add_field() under-the-hood.\n            self.model._meta.set_primary_key(self.name, field)\n        else:\n            self.model._meta.add_field(self.name, field)\n\n    @staticmethod\n    def resolve(model_cls):\n        unresolved = sorted(DeferredForeignKey._unresolved,\n                            key=operator.attrgetter('_order'))\n        for dr in unresolved:\n            if dr.rel_model_name == model_cls.__name__.lower():\n                dr.set_model(model_cls)\n                DeferredForeignKey._unresolved.discard(dr)\n\n\nclass DeferredThroughModel(object):\n    def __init__(self):\n        self._refs = []\n\n    def set_field(self, model, field, name):\n        self._refs.append((model, field, name))\n\n    def set_model(self, through_model):\n        for src_model, m2mfield, name in self._refs:\n            m2mfield.through_model = through_model\n            src_model._meta.add_field(name, m2mfield)\n\n\nclass MetaField(Field):\n    column_name = default = model = name = None\n    primary_key = False\n\n\nclass ManyToManyFieldAccessor(FieldAccessor):\n    def __init__(self, model, field, name):\n        super(ManyToManyFieldAccessor, self).__init__(model, field, name)\n        self.model = field.model\n        self.rel_model = field.rel_model\n        self.through_model = field.through_model\n        src_fks = self.through_model._meta.model_refs[self.model]\n        dest_fks = self.through_model._meta.model_refs[self.rel_model]\n        if not src_fks:\n            raise ValueError('Cannot find foreign-key to \"%s\" on \"%s\" model.' %\n                             (self.model, self.through_model))\n        elif not dest_fks:\n            raise ValueError('Cannot find foreign-key to \"%s\" on \"%s\" model.' %\n                             (self.rel_model, self.through_model))\n        self.src_fk = src_fks[0]\n        self.dest_fk = dest_fks[0]\n\n    def __get__(self, instance, instance_type=None, force_query=False):\n        if instance is not None:\n            if not force_query and self.src_fk.backref != '+':\n                backref = getattr(instance, self.src_fk.backref)\n                if isinstance(backref, list):\n                    return [getattr(obj, self.dest_fk.name) for obj in backref]\n\n            src_id = getattr(instance, self.src_fk.rel_field.name)\n            if src_id is None and self.field._prevent_unsaved:\n                raise ValueError('Cannot get many-to-many \"%s\" for unsaved '\n                                 'instance \"%s\".' % (self.field, instance))\n            return (ManyToManyQuery(instance, self, self.rel_model)\n                    .join(self.through_model)\n                    .join(self.model)\n                    .where(self.src_fk == src_id))\n\n        return self.field\n\n    def __set__(self, instance, value):\n        src_id = getattr(instance, self.src_fk.rel_field.name)\n        if src_id is None and self.field._prevent_unsaved:\n            raise ValueError('Cannot set many-to-many \"%s\" for unsaved '\n                             'instance \"%s\".' % (self.field, instance))\n        query = self.__get__(instance, force_query=True)\n        query.add(value, clear_existing=True)\n\n\nclass ManyToManyField(MetaField):\n    accessor_class = ManyToManyFieldAccessor\n\n    def __init__(self, model, backref=None, through_model=None, on_delete=None,\n                 on_update=None, prevent_unsaved=True, _is_backref=False):\n        if through_model is not None:\n            if not (isinstance(through_model, DeferredThroughModel) or\n                    is_model(through_model)):\n                raise TypeError('Unexpected value for through_model. Expected '\n                                'Model or DeferredThroughModel.')\n            if not _is_backref and (on_delete is not None or on_update is not None):\n                raise ValueError('Cannot specify on_delete or on_update when '\n                                 'through_model is specified.')\n        self.rel_model = model\n        self.backref = backref\n        self._through_model = through_model\n        self._on_delete = on_delete\n        self._on_update = on_update\n        self._prevent_unsaved = prevent_unsaved\n        self._is_backref = _is_backref\n\n    def _get_descriptor(self):\n        return ManyToManyFieldAccessor(self)\n\n    def bind(self, model, name, set_attribute=True):\n        if isinstance(self._through_model, DeferredThroughModel):\n            self._through_model.set_field(model, self, name)\n            return\n\n        super(ManyToManyField, self).bind(model, name, set_attribute)\n\n        if not self._is_backref:\n            many_to_many_field = ManyToManyField(\n                self.model,\n                backref=name,\n                through_model=self.through_model,\n                on_delete=self._on_delete,\n                on_update=self._on_update,\n                _is_backref=True)\n            self.backref = self.backref or model._meta.name + 's'\n            self.rel_model._meta.add_field(self.backref, many_to_many_field)\n\n    def get_models(self):\n        return [model for _, model in sorted((\n            (self._is_backref, self.model),\n            (not self._is_backref, self.rel_model)))]\n\n    @property\n    def through_model(self):\n        if self._through_model is None:\n            self._through_model = self._create_through_model()\n        return self._through_model\n\n    @through_model.setter\n    def through_model(self, value):\n        self._through_model = value\n\n    def _create_through_model(self):\n        lhs, rhs = self.get_models()\n        tables = [model._meta.table_name for model in (lhs, rhs)]\n\n        class Meta:\n            database = self.model._meta.database\n            schema = self.model._meta.schema\n            table_name = '%s_%s_through' % tuple(tables)\n            indexes = (\n                ((lhs._meta.name, rhs._meta.name),\n                 True),)\n\n        params = {'on_delete': self._on_delete, 'on_update': self._on_update}\n        attrs = {\n            lhs._meta.name: ForeignKeyField(lhs, **params),\n            rhs._meta.name: ForeignKeyField(rhs, **params),\n            'Meta': Meta}\n\n        klass_name = '%s%sThrough' % (lhs.__name__, rhs.__name__)\n        return type(klass_name, (Model,), attrs)\n\n    def get_through_model(self):\n        # XXX: Deprecated. Just use the \"through_model\" property.\n        return self.through_model\n\n\nclass VirtualField(MetaField):\n    field_class = None\n\n    def __init__(self, field_class=None, *args, **kwargs):\n        Field = field_class if field_class is not None else self.field_class\n        self.field_instance = Field() if Field is not None else None\n        super(VirtualField, self).__init__(*args, **kwargs)\n\n    def db_value(self, value):\n        if self.field_instance is not None:\n            return self.field_instance.db_value(value)\n        return value\n\n    def python_value(self, value):\n        if self.field_instance is not None:\n            return self.field_instance.python_value(value)\n        return value\n\n    def bind(self, model, name, set_attribute=True):\n        self.model = model\n        self.column_name = self.name = self.safe_name = name\n        setattr(model, name, self.accessor_class(model, self, name))\n\n\nclass CompositeKey(MetaField):\n    sequence = None\n\n    def __init__(self, *field_names):\n        self.field_names = field_names\n        self._safe_field_names = None\n\n    @property\n    def safe_field_names(self):\n        if self._safe_field_names is None:\n            if self.model is None:\n                return self.field_names\n\n            self._safe_field_names = [self.model._meta.fields[f].safe_name\n                                      for f in self.field_names]\n        return self._safe_field_names\n\n    def __get__(self, instance, instance_type=None):\n        if instance is not None:\n            return tuple([getattr(instance, f) for f in self.safe_field_names])\n        return self\n\n    def __set__(self, instance, value):\n        if not isinstance(value, (list, tuple)):\n            raise TypeError('A list or tuple must be used to set the value of '\n                            'a composite primary key.')\n        if len(value) != len(self.field_names):\n            raise ValueError('The length of the value must equal the number '\n                             'of columns of the composite primary key.')\n        for idx, field_value in enumerate(value):\n            setattr(instance, self.field_names[idx], field_value)\n\n    def __eq__(self, other):\n        expressions = [(self.model._meta.fields[field] == value)\n                       for field, value in zip(self.field_names, other)]\n        return reduce(operator.and_, expressions)\n\n    def __ne__(self, other):\n        return ~(self == other)\n\n    def __hash__(self):\n        return hash((self.model.__name__, self.field_names))\n\n    def __sql__(self, ctx):\n        # If the composite PK is being selected, do not use parens. Elsewhere,\n        # such as in an expression, we want to use parentheses and treat it as\n        # a row value.\n        parens = ctx.scope != SCOPE_SOURCE\n        return ctx.sql(NodeList([self.model._meta.fields[field]\n                                 for field in self.field_names], ', ', parens))\n\n    def bind(self, model, name, set_attribute=True):\n        self.model = model\n        self.column_name = self.name = self.safe_name = name\n        setattr(model, self.name, self)\n\n\nclass _SortedFieldList(object):\n    __slots__ = ('_keys', '_items')\n\n    def __init__(self):\n        self._keys = []\n        self._items = []\n\n    def __getitem__(self, i):\n        return self._items[i]\n\n    def __iter__(self):\n        return iter(self._items)\n\n    def __contains__(self, item):\n        k = item._sort_key\n        i = bisect_left(self._keys, k)\n        j = bisect_right(self._keys, k)\n        return item in self._items[i:j]\n\n    def index(self, field):\n        return self._keys.index(field._sort_key)\n\n    def insert(self, item):\n        k = item._sort_key\n        i = bisect_left(self._keys, k)\n        self._keys.insert(i, k)\n        self._items.insert(i, item)\n\n    def remove(self, item):\n        idx = self.index(item)\n        del self._items[idx]\n        del self._keys[idx]\n\n\n# MODELS\n\n\nclass SchemaManager(object):\n    def __init__(self, model, database=None, **context_options):\n        self.model = model\n        self._database = database\n        context_options.setdefault('scope', SCOPE_VALUES)\n        self.context_options = context_options\n\n    @property\n    def database(self):\n        db = self._database or self.model._meta.database\n        if db is None:\n            raise ImproperlyConfigured('database attribute does not appear to '\n                                       'be set on the model: %s' % self.model)\n        return db\n\n    @database.setter\n    def database(self, value):\n        self._database = value\n\n    def _create_context(self):\n        return self.database.get_sql_context(**self.context_options)\n\n    def _create_table(self, safe=True, **options):\n        is_temp = options.pop('temporary', False)\n        ctx = self._create_context()\n        ctx.literal('CREATE TEMPORARY TABLE ' if is_temp else 'CREATE TABLE ')\n        if safe:\n            ctx.literal('IF NOT EXISTS ')\n        ctx.sql(self.model).literal(' ')\n\n        columns = []\n        constraints = []\n        meta = self.model._meta\n        if meta.composite_key:\n            pk_columns = [meta.fields[field_name].column\n                          for field_name in meta.primary_key.field_names]\n            constraints.append(NodeList((SQL('PRIMARY KEY'),\n                                         EnclosedNodeList(pk_columns))))\n\n        for field in meta.sorted_fields:\n            columns.append(field.ddl(ctx))\n            if isinstance(field, ForeignKeyField) and not field.deferred:\n                constraints.append(field.foreign_key_constraint())\n\n        if meta.constraints:\n            constraints.extend(meta.constraints)\n\n        constraints.extend(self._create_table_option_sql(options))\n        ctx.sql(EnclosedNodeList(columns + constraints))\n\n        if meta.table_settings is not None:\n            table_settings = ensure_tuple(meta.table_settings)\n            for setting in table_settings:\n                if not isinstance(setting, str):\n                    raise ValueError('table_settings must be strings')\n                ctx.literal(' ').literal(setting)\n\n        extra_opts = []\n        if meta.strict_tables: extra_opts.append('STRICT')\n        if meta.without_rowid: extra_opts.append('WITHOUT ROWID')\n        if extra_opts:\n            ctx.literal(' %s' % ', '.join(extra_opts))\n        return ctx\n\n    def _create_table_option_sql(self, options):\n        accum = []\n        options = merge_dict(self.model._meta.options or {}, options)\n        if not options:\n            return accum\n\n        for key, value in sorted(options.items()):\n            if not isinstance(value, Node):\n                if is_model(value):\n                    value = value._meta.table\n                else:\n                    value = SQL(str(value))\n            accum.append(NodeList((SQL(key), value), glue='='))\n        return accum\n\n    def create_table(self, safe=True, **options):\n        self.database.execute(self._create_table(safe=safe, **options))\n\n    def _create_table_as(self, table_name, query, safe=True, **meta):\n        ctx = (self._create_context()\n               .literal('CREATE TEMPORARY TABLE '\n                        if meta.get('temporary') else 'CREATE TABLE '))\n        if safe:\n            ctx.literal('IF NOT EXISTS ')\n        return (ctx\n                .sql(Entity(*ensure_tuple(table_name)))\n                .literal(' AS ')\n                .sql(query))\n\n    def create_table_as(self, table_name, query, safe=True, **meta):\n        ctx = self._create_table_as(table_name, query, safe=safe, **meta)\n        self.database.execute(ctx)\n\n    def _drop_table(self, safe=True, **options):\n        ctx = (self._create_context()\n               .literal('DROP TABLE IF EXISTS ' if safe else 'DROP TABLE ')\n               .sql(self.model))\n        if options.get('cascade'):\n            ctx = ctx.literal(' CASCADE')\n        elif options.get('restrict'):\n            ctx = ctx.literal(' RESTRICT')\n        return ctx\n\n    def drop_table(self, safe=True, **options):\n        self.database.execute(self._drop_table(safe=safe, **options))\n\n    def _truncate_table(self, restart_identity=False, cascade=False):\n        db = self.database\n        if not db.truncate_table:\n            return (self._create_context()\n                    .literal('DELETE FROM ').sql(self.model))\n\n        ctx = self._create_context().literal('TRUNCATE TABLE ').sql(self.model)\n        if restart_identity:\n            ctx = ctx.literal(' RESTART IDENTITY')\n        if cascade:\n            ctx = ctx.literal(' CASCADE')\n        return ctx\n\n    def truncate_table(self, restart_identity=False, cascade=False):\n        self.database.execute(self._truncate_table(restart_identity, cascade))\n\n    def _create_indexes(self, safe=True):\n        return [self._create_index(index, safe)\n                for index in self.model._meta.fields_to_index()]\n\n    def _create_index(self, index, safe=True):\n        if isinstance(index, Index):\n            if not self.database.safe_create_index:\n                index = index.safe(False)\n            elif index._safe != safe:\n                index = index.safe(safe)\n            if isinstance(self._database, SqliteDatabase):\n                # Ensure we do not use value placeholders with Sqlite, as they\n                # are not supported.\n                index = ValueLiterals(index)\n        return self._create_context().sql(index)\n\n    def create_indexes(self, safe=True):\n        for query in self._create_indexes(safe=safe):\n            self.database.execute(query)\n\n    def _drop_indexes(self, safe=True):\n        return [self._drop_index(index, safe)\n                for index in self.model._meta.fields_to_index()\n                if isinstance(index, Index)]\n\n    def _drop_index(self, index, safe):\n        statement = 'DROP INDEX '\n        if safe and self.database.safe_drop_index:\n            statement += 'IF EXISTS '\n        if isinstance(index._table, Table) and index._table._schema:\n            index_name = Entity(index._table._schema, index._name)\n        else:\n            index_name = Entity(index._name)\n        return (self\n                ._create_context()\n                .literal(statement)\n                .sql(index_name))\n\n    def drop_indexes(self, safe=True):\n        for query in self._drop_indexes(safe=safe):\n            self.database.execute(query)\n\n    def _check_sequences(self, field):\n        if not field.sequence or not self.database.sequences:\n            raise ValueError('Sequences are either not supported, or are not '\n                             'defined for \"%s\".' % field.name)\n\n    def _sequence_for_field(self, field):\n        if field.model._meta.schema:\n            return Entity(field.model._meta.schema, field.sequence)\n        else:\n            return Entity(field.sequence)\n\n    def _create_sequence(self, field):\n        self._check_sequences(field)\n        if not self.database.sequence_exists(field.sequence):\n            return (self\n                    ._create_context()\n                    .literal('CREATE SEQUENCE ')\n                    .sql(self._sequence_for_field(field)))\n\n    def create_sequence(self, field):\n        seq_ctx = self._create_sequence(field)\n        if seq_ctx is not None:\n            self.database.execute(seq_ctx)\n\n    def _drop_sequence(self, field):\n        self._check_sequences(field)\n        if self.database.sequence_exists(field.sequence):\n            return (self\n                    ._create_context()\n                    .literal('DROP SEQUENCE ')\n                    .sql(self._sequence_for_field(field)))\n\n    def drop_sequence(self, field):\n        seq_ctx = self._drop_sequence(field)\n        if seq_ctx is not None:\n            self.database.execute(seq_ctx)\n\n    def _create_foreign_key(self, field):\n        return (self\n                ._create_context()\n                .literal('ALTER TABLE ')\n                .sql(field.model)\n                .literal(' ADD ')\n                .sql(field.foreign_key_constraint(True)))\n\n    def create_foreign_key(self, field):\n        self.database.execute(self._create_foreign_key(field))\n\n    def create_sequences(self):\n        if self.database.sequences:\n            for field in self.model._meta.sorted_fields:\n                if field.sequence:\n                    self.create_sequence(field)\n\n    def create_all(self, safe=True, **table_options):\n        self.create_sequences()\n        self.create_table(safe, **table_options)\n        self.create_indexes(safe=safe)\n\n    def drop_sequences(self):\n        if self.database.sequences:\n            for field in self.model._meta.sorted_fields:\n                if field.sequence:\n                    self.drop_sequence(field)\n\n    def drop_all(self, safe=True, drop_sequences=True, **options):\n        self.drop_table(safe, **options)\n        if drop_sequences:\n            self.drop_sequences()\n\n\nclass Metadata(object):\n    def __init__(self, model, database=None, table_name=None, indexes=None,\n                 primary_key=None, constraints=None, schema=None,\n                 only_save_dirty=False, depends_on=None, options=None,\n                 db_table=None, table_function=None, table_settings=None,\n                 without_rowid=False, temporary=False, strict_tables=None,\n                 legacy_table_names=True, **kwargs):\n        if db_table is not None:\n            __deprecated__('\"db_table\" has been deprecated in favor of '\n                           '\"table_name\" for Models.')\n            table_name = db_table\n        self.model = model\n        self.database = database\n\n        self.fields = {}\n        self.columns = {}\n        self.combined = {}\n\n        self._sorted_field_list = _SortedFieldList()\n        self.sorted_fields = []\n        self.sorted_field_names = []\n\n        self.defaults = {}\n        self._default_by_name = {}\n        self._default_dict = {}\n        self._default_callables = {}\n        self._default_callable_list = []\n\n        self.name = model.__name__.lower()\n        self.table_function = table_function\n        self.legacy_table_names = legacy_table_names\n        if not table_name:\n            table_name = (self.table_function(model)\n                          if self.table_function\n                          else self.make_table_name())\n        self.table_name = table_name\n        self._table = None\n\n        self.indexes = list(indexes) if indexes else []\n        self.constraints = constraints\n        self._schema = schema\n        self.primary_key = primary_key\n        self.composite_key = self.auto_increment = None\n        self.only_save_dirty = only_save_dirty\n        self.depends_on = depends_on\n        self.table_settings = table_settings\n        self.without_rowid = without_rowid\n        self.strict_tables = strict_tables\n        self.temporary = temporary\n\n        self.refs = {}\n        self.backrefs = {}\n        self.model_refs = collections.defaultdict(list)\n        self.model_backrefs = collections.defaultdict(list)\n        self.manytomany = {}\n\n        self.options = options or {}\n        for key, value in kwargs.items():\n            setattr(self, key, value)\n        self._additional_keys = set(kwargs.keys())\n\n        # Allow objects to register hooks that are called if the model is bound\n        # to a different database. For example, BlobField uses a different\n        # Python data-type depending on the db driver / python version. When\n        # the database changes, we need to update any BlobField so they can use\n        # the appropriate data-type.\n        self._db_hooks = []\n\n    def make_table_name(self):\n        if self.legacy_table_names:\n            return re.sub(r'[^\\w]+', '_', self.name)\n        return make_snake_case(self.model.__name__)\n\n    def model_graph(self, refs=True, backrefs=True, depth_first=True):\n        if not refs and not backrefs:\n            raise ValueError('One of `refs` or `backrefs` must be True.')\n\n        accum = [(None, self.model, None)]\n        seen = set()\n        queue = collections.deque((self,))\n        method = queue.pop if depth_first else queue.popleft\n\n        while queue:\n            curr = method()\n            if curr in seen: continue\n            seen.add(curr)\n\n            if refs:\n                for fk, model in curr.refs.items():\n                    accum.append((fk, model, False))\n                    queue.append(model._meta)\n            if backrefs:\n                for fk, model in curr.backrefs.items():\n                    accum.append((fk, model, True))\n                    queue.append(model._meta)\n\n        return accum\n\n    def add_ref(self, field):\n        rel = field.rel_model\n        self.refs[field] = rel\n        self.model_refs[rel].append(field)\n        rel._meta.backrefs[field] = self.model\n        rel._meta.model_backrefs[self.model].append(field)\n\n    def remove_ref(self, field):\n        rel = field.rel_model\n        del self.refs[field]\n        self.model_refs[rel].remove(field)\n        del rel._meta.backrefs[field]\n        rel._meta.model_backrefs[self.model].remove(field)\n\n    def add_manytomany(self, field):\n        self.manytomany[field.name] = field\n\n    def remove_manytomany(self, field):\n        del self.manytomany[field.name]\n\n    @property\n    def table(self):\n        if self._table is None:\n            self._table = Table(\n                self.table_name,\n                [field.column_name for field in self.sorted_fields],\n                schema=self.schema,\n                _model=self.model,\n                _database=self.database)\n        return self._table\n\n    @table.setter\n    def table(self, value):\n        raise AttributeError('Cannot set the \"table\".')\n\n    @table.deleter\n    def table(self):\n        self._table = None\n\n    @property\n    def schema(self):\n        return self._schema\n\n    @schema.setter\n    def schema(self, value):\n        self._schema = value\n        del self.table\n\n    @property\n    def entity(self):\n        if self._schema:\n            return Entity(self._schema, self.table_name)\n        else:\n            return Entity(self.table_name)\n\n    def _update_sorted_fields(self):\n        self.sorted_fields = list(self._sorted_field_list)\n        self.sorted_field_names = [f.name for f in self.sorted_fields]\n\n    def get_rel_for_model(self, model):\n        if isinstance(model, ModelAlias):\n            model = model.model\n        forwardrefs = self.model_refs.get(model, [])\n        backrefs = self.model_backrefs.get(model, [])\n        return (forwardrefs, backrefs)\n\n    def add_field(self, field_name, field, set_attribute=True):\n        if field_name in self.fields:\n            self.remove_field(field_name)\n        elif field_name in self.manytomany:\n            self.remove_manytomany(self.manytomany[field_name])\n\n        if not isinstance(field, MetaField):\n            del self.table\n            field.bind(self.model, field_name, set_attribute)\n            self.fields[field.name] = field\n            self.columns[field.column_name] = field\n            self.combined[field.name] = field\n            self.combined[field.column_name] = field\n\n            self._sorted_field_list.insert(field)\n            self._update_sorted_fields()\n\n            if field.default is not None:\n                # This optimization helps speed up model instance construction.\n                self.defaults[field] = field.default\n                if callable_(field.default):\n                    self._default_callables[field] = field.default\n                    self._default_callable_list.append((field.name,\n                                                        field.default))\n                else:\n                    self._default_dict[field] = field.default\n                    self._default_by_name[field.name] = field.default\n        else:\n            field.bind(self.model, field_name, set_attribute)\n\n        if isinstance(field, ForeignKeyField):\n            self.add_ref(field)\n        elif isinstance(field, ManyToManyField) and field.name:\n            self.add_manytomany(field)\n\n    def remove_field(self, field_name):\n        if field_name not in self.fields:\n            return\n\n        del self.table\n        original = self.fields.pop(field_name)\n        del self.columns[original.column_name]\n        del self.combined[field_name]\n        try:\n            del self.combined[original.column_name]\n        except KeyError:\n            pass\n        self._sorted_field_list.remove(original)\n        self._update_sorted_fields()\n\n        if original.default is not None:\n            del self.defaults[original]\n            if self._default_callables.pop(original, None):\n                for i, (name, _) in enumerate(self._default_callable_list):\n                    if name == field_name:\n                        self._default_callable_list.pop(i)\n                        break\n            else:\n                self._default_dict.pop(original, None)\n                self._default_by_name.pop(original.name, None)\n\n        if isinstance(original, ForeignKeyField):\n            self.remove_ref(original)\n\n    def set_primary_key(self, name, field):\n        self.composite_key = isinstance(field, CompositeKey)\n        self.add_field(name, field)\n        self.primary_key = field\n        self.auto_increment = (\n            field.auto_increment or\n            bool(field.sequence))\n\n    def get_primary_keys(self):\n        if self.composite_key:\n            return tuple([self.fields[field_name]\n                          for field_name in self.primary_key.field_names])\n        else:\n            return (self.primary_key,) if self.primary_key is not False else ()\n\n    def get_default_dict(self):\n        dd = self._default_by_name.copy()\n        for field_name, default in self._default_callable_list:\n            dd[field_name] = default()\n        return dd\n\n    def fields_to_index(self):\n        indexes = []\n        for f in self.sorted_fields:\n            if f.primary_key:\n                continue\n            if f.index or f.unique:\n                indexes.append(ModelIndex(self.model, (f,), unique=f.unique,\n                                          using=f.index_type))\n\n        for index_obj in self.indexes:\n            if isinstance(index_obj, Node):\n                indexes.append(index_obj)\n            elif isinstance(index_obj, (list, tuple)):\n                index_parts, unique = index_obj\n                fields = []\n                for part in index_parts:\n                    if isinstance(part, str):\n                        fields.append(self.combined[part])\n                    elif isinstance(part, Node):\n                        fields.append(part)\n                    else:\n                        raise ValueError('Expected either a field name or a '\n                                         'subclass of Node. Got: %s' % part)\n                indexes.append(ModelIndex(self.model, fields, unique=unique))\n\n        return indexes\n\n    def set_database(self, database):\n        self.database = database\n        self.model._schema._database = database\n        del self.table\n\n        # Apply any hooks that have been registered. If we have an\n        # uninitialized proxy object, we will treat that as `None`.\n        if isinstance(database, Proxy) and database.obj is None:\n            database = None\n\n        for hook in self._db_hooks:\n            hook(database)\n\n    def set_table_name(self, table_name):\n        self.table_name = table_name\n        del self.table\n\n\nclass SubclassAwareMetadata(Metadata):\n    models = []\n\n    def __init__(self, model, *args, **kwargs):\n        super(SubclassAwareMetadata, self).__init__(model, *args, **kwargs)\n        self.models.append(model)\n\n    def map_models(self, fn):\n        for model in self.models:\n            fn(model)\n\n\nclass DoesNotExist(Exception): pass\n\n\nclass ModelBase(type):\n    inheritable = set(['constraints', 'database', 'indexes', 'primary_key',\n                       'options', 'schema', 'table_function', 'temporary',\n                       'only_save_dirty', 'legacy_table_names',\n                       'table_settings', 'strict_tables'])\n\n    def __new__(cls, name, bases, attrs, **kwargs):\n        if name == MODEL_BASE or bases[0].__name__ == MODEL_BASE:\n            return super(ModelBase, cls).__new__(cls, name, bases, attrs,\n                                                 **kwargs)\n\n        meta_options = {}\n        meta = attrs.pop('Meta', None)\n        if meta:\n            for k, v in meta.__dict__.items():\n                if not k.startswith('_'):\n                    meta_options[k] = v\n\n        pk = getattr(meta, 'primary_key', None)\n        pk_name = parent_pk = None\n\n        # Inherit any field descriptors by deep copying the underlying field\n        # into the attrs of the new model, additionally see if the bases define\n        # inheritable model options and swipe them.\n        for b in bases:\n            if not hasattr(b, '_meta'):\n                continue\n\n            base_meta = b._meta\n            if parent_pk is None:\n                parent_pk = deepcopy(base_meta.primary_key)\n            all_inheritable = cls.inheritable | base_meta._additional_keys\n            for k in base_meta.__dict__:\n                if k in all_inheritable and k not in meta_options:\n                    meta_options[k] = base_meta.__dict__[k]\n            meta_options.setdefault('database', base_meta.database)\n            meta_options.setdefault('schema', base_meta.schema)\n\n            for (k, v) in b.__dict__.items():\n                if k in attrs: continue\n\n                if isinstance(v, FieldAccessor) and not v.field.primary_key:\n                    attrs[k] = deepcopy(v.field)\n\n        sopts = meta_options.pop('schema_options', None) or {}\n        Meta = meta_options.get('model_metadata_class', Metadata)\n        Schema = meta_options.get('schema_manager_class', SchemaManager)\n\n        # Construct the new class.\n        cls = super(ModelBase, cls).__new__(cls, name, bases, attrs, **kwargs)\n        cls.__data__ = cls.__rel__ = None\n\n        cls._meta = Meta(cls, **meta_options)\n        cls._schema = Schema(cls, **sopts)\n\n        fields = []\n        for key, value in cls.__dict__.items():\n            if isinstance(value, Field):\n                if value.primary_key and pk:\n                    raise ValueError('over-determined primary key %s.' % name)\n                elif value.primary_key:\n                    pk, pk_name = value, key\n                else:\n                    fields.append((key, value))\n\n        if pk is None:\n            if parent_pk is not False:\n                pk, pk_name = ((parent_pk, parent_pk.name)\n                               if parent_pk is not None else\n                               (AutoField(), 'id'))\n            else:\n                pk = False\n        elif isinstance(pk, CompositeKey):\n            pk_name = '__composite_key__'\n            cls._meta.composite_key = True\n\n        if pk is not False:\n            cls._meta.set_primary_key(pk_name, pk)\n\n        for name, field in fields:\n            cls._meta.add_field(name, field)\n\n        # Create a repr and error class before finalizing.\n        if hasattr(cls, '__str__') and '__repr__' not in attrs:\n            setattr(cls, '__repr__', lambda self: '<%s: %s>' % (\n                cls.__name__, self.__str__()))\n\n        exc_name = '%sDoesNotExist' % cls.__name__\n        exc_attrs = {'__module__': cls.__module__}\n        exception_class = type(exc_name, (DoesNotExist,), exc_attrs)\n        cls.DoesNotExist = exception_class\n\n        # Call validation hook, allowing additional model validation.\n        cls.validate_model()\n        DeferredForeignKey.resolve(cls)\n        return cls\n\n    def __repr__(self):\n        return '<Model: %s>' % self.__name__\n\n    def __iter__(self):\n        return iter(self.select())\n\n    def __getitem__(self, key):\n        return self.get_by_id(key)\n\n    def __setitem__(self, key, value):\n        self.set_by_id(key, value)\n\n    def __delitem__(self, key):\n        self.delete_by_id(key)\n\n    def __contains__(self, key):\n        try:\n            self.get_by_id(key)\n        except self.DoesNotExist:\n            return False\n        else:\n            return True\n\n    def __len__(self):\n        return self.select().count()\n    def __bool__(self): return True\n    __nonzero__ = __bool__  # Python 2.\n\n    def __sql__(self, ctx):\n        return ctx.sql(self._meta.table)\n\n\nclass _BoundModelsContext(object):\n    def __init__(self, models, database, bind_refs, bind_backrefs):\n        self.models = models\n        self.database = database\n        self.bind_refs = bind_refs\n        self.bind_backrefs = bind_backrefs\n\n    def __enter__(self):\n        self._orig_database = []\n        for model in self.models:\n            self._orig_database.append(model._meta.database)\n            model.bind(self.database, self.bind_refs, self.bind_backrefs,\n                       _exclude=set(self.models))\n        return self.models\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        for model, db in zip(self.models, self._orig_database):\n            model.bind(db, self.bind_refs, self.bind_backrefs,\n                       _exclude=set(self.models))\n\n\nclass Model(with_metaclass(ModelBase, Node)):\n    def __init__(self, *args, **kwargs):\n        if kwargs.pop('__no_default__', None):\n            self.__data__ = {}\n            self._dirty = set()\n        else:\n            self.__data__ = self._meta.get_default_dict()\n            self._dirty = set(self.__data__)\n        self.__rel__ = {}\n\n        for k in kwargs:\n            setattr(self, k, kwargs[k])\n\n    def __str__(self):\n        return str(self._pk) if self._meta.primary_key is not False else 'n/a'\n\n    @classmethod\n    def validate_model(cls):\n        pass\n\n    @classmethod\n    def alias(cls, alias=None):\n        return ModelAlias(cls, alias)\n\n    @classmethod\n    def select(cls, *fields):\n        is_default = not fields\n        if not fields:\n            fields = cls._meta.sorted_fields\n        return ModelSelect(cls, fields, is_default=is_default)\n\n    @classmethod\n    def _normalize_data(cls, data, kwargs):\n        normalized = {}\n        if data:\n            if not isinstance(data, dict):\n                if kwargs:\n                    raise ValueError('Data cannot be mixed with keyword '\n                                     'arguments: %s' % data)\n                return data\n            for key in data:\n                try:\n                    field = (key if isinstance(key, Field)\n                             else cls._meta.combined[key])\n                except KeyError:\n                    if not isinstance(key, Node):\n                        raise ValueError('Unrecognized field name: \"%s\" in %s.'\n                                         % (key, data))\n                    field = key\n                normalized[field] = data[key]\n        if kwargs:\n            for key in kwargs:\n                try:\n                    normalized[cls._meta.combined[key]] = kwargs[key]\n                except KeyError:\n                    normalized[getattr(cls, key)] = kwargs[key]\n        return normalized\n\n    @classmethod\n    def update(cls, __data=None, **update):\n        return ModelUpdate(cls, cls._normalize_data(__data, update))\n\n    @classmethod\n    def insert(cls, __data=None, **insert):\n        return ModelInsert(cls, cls._normalize_data(__data, insert))\n\n    @classmethod\n    def insert_many(cls, rows, fields=None):\n        return ModelInsert(cls, insert=rows, columns=fields)\n\n    @classmethod\n    def insert_from(cls, query, fields):\n        columns = [getattr(cls, field) if isinstance(field, str)\n                   else field for field in fields]\n        return ModelInsert(cls, insert=query, columns=columns)\n\n    @classmethod\n    def replace(cls, __data=None, **insert):\n        return cls.insert(__data, **insert).on_conflict('REPLACE')\n\n    @classmethod\n    def replace_many(cls, rows, fields=None):\n        return (cls\n                .insert_many(rows=rows, fields=fields)\n                .on_conflict('REPLACE'))\n\n    @classmethod\n    def raw(cls, sql, *params):\n        return ModelRaw(cls, sql, params)\n\n    @classmethod\n    def delete(cls):\n        return ModelDelete(cls)\n\n    @classmethod\n    def create(cls, **query):\n        inst = cls(**query)\n        inst.save(force_insert=True)\n        return inst\n\n    @classmethod\n    def bulk_create(cls, model_list, batch_size=None):\n        if batch_size is not None:\n            batches = chunked(model_list, batch_size)\n        else:\n            batches = [model_list]\n\n        field_names = list(cls._meta.sorted_field_names)\n        if cls._meta.auto_increment:\n            pk_name = cls._meta.primary_key.name\n            field_names.remove(pk_name)\n\n        if cls._meta.database.returning_clause and \\\n           cls._meta.primary_key is not False:\n            pk_fields = cls._meta.get_primary_keys()\n        else:\n            pk_fields = None\n\n        fields = [cls._meta.fields[field_name] for field_name in field_names]\n        attrs = []\n        for field in fields:\n            if isinstance(field, ForeignKeyField):\n                attrs.append(field.object_id_name)\n            else:\n                attrs.append(field.name)\n\n        for batch in batches:\n            accum = ([getattr(model, f) for f in attrs]\n                     for model in batch)\n            res = cls.insert_many(accum, fields=fields).execute()\n            if pk_fields and res is not None:\n                for row, model in zip(res, batch):\n                    for (pk_field, obj_id) in zip(pk_fields, row):\n                        setattr(model, pk_field.name, obj_id)\n\n    @classmethod\n    def bulk_update(cls, model_list, fields, batch_size=None):\n        if isinstance(cls._meta.primary_key, CompositeKey):\n            raise ValueError('bulk_update() is not supported for models with '\n                             'a composite primary key.')\n\n        # First normalize list of fields so all are field instances.\n        fields = [cls._meta.fields[f] if isinstance(f, str) else f\n                  for f in fields]\n        # Now collect list of attribute names to use for values.\n        attrs = [field.object_id_name if isinstance(field, ForeignKeyField)\n                 else field.name for field in fields]\n\n        if batch_size is not None:\n            batches = chunked(model_list, batch_size)\n        else:\n            batches = [model_list]\n\n        n = 0\n        pk = cls._meta.primary_key\n\n        for batch in batches:\n            id_list = [model._pk for model in batch]\n            update = {}\n            for field, attr in zip(fields, attrs):\n                accum = []\n                for model in batch:\n                    value = getattr(model, attr)\n                    if not isinstance(value, Node):\n                        value = field.to_value(value, case=True)\n                    accum.append((pk.to_value(model._pk), value))\n                case = Case(pk, accum)\n                update[field] = case\n\n            n += (cls.update(update)\n                  .where(cls._meta.primary_key.in_(id_list))\n                  .execute())\n        return n\n\n    @classmethod\n    def noop(cls):\n        return NoopModelSelect(cls, ())\n\n    @classmethod\n    def get(cls, *query, **filters):\n        sq = cls.select()\n        if query:\n            # Handle simple lookup using just the primary key.\n            if len(query) == 1 and isinstance(query[0], int) and \\\n               cls._meta.auto_increment:\n                sq = sq.where(cls._meta.primary_key == query[0])\n            else:\n                sq = sq.where(*query)\n        if filters:\n            sq = sq.filter(**filters)\n        return sq.get()\n\n    @classmethod\n    def get_or_none(cls, *query, **filters):\n        try:\n            return cls.get(*query, **filters)\n        except DoesNotExist:\n            pass\n\n    @classmethod\n    def get_by_id(cls, pk):\n        return cls.get(cls._meta.primary_key == pk)\n\n    @classmethod\n    def set_by_id(cls, key, value):\n        if key is None:\n            return cls.insert(value).execute()\n        else:\n            return (cls.update(value)\n                    .where(cls._meta.primary_key == key).execute())\n\n    @classmethod\n    def delete_by_id(cls, pk):\n        return cls.delete().where(cls._meta.primary_key == pk).execute()\n\n    @classmethod\n    def get_or_create(cls, **kwargs):\n        defaults = kwargs.pop('defaults', {})\n        query = cls.select()\n        for field, value in kwargs.items():\n            query = query.where(getattr(cls, field) == value)\n\n        try:\n            return query.get(), False\n        except cls.DoesNotExist:\n            try:\n                if defaults:\n                    kwargs.update(defaults)\n                with cls._meta.database.atomic():\n                    return cls.create(**kwargs), True\n            except IntegrityError as exc:\n                try:\n                    return query.get(), False\n                except cls.DoesNotExist:\n                    raise exc\n\n    @classmethod\n    def filter(cls, *dq_nodes, **filters):\n        return cls.select().filter(*dq_nodes, **filters)\n\n    def get_id(self):\n        # Using getattr(self, pk-name) could accidentally trigger a query if\n        # the primary-key is a foreign-key. So we use the safe_name attribute,\n        # which defaults to the field-name, but will be the object_id_name for\n        # foreign-key fields.\n        if self._meta.primary_key is not False:\n            return getattr(self, self._meta.primary_key.safe_name)\n\n    _pk = property(get_id)\n\n    @_pk.setter\n    def _pk(self, value):\n        setattr(self, self._meta.primary_key.name, value)\n\n    def _pk_expr(self):\n        return self._meta.primary_key == self._pk\n\n    def _prune_fields(self, field_dict, only):\n        new_data = {}\n        for field in only:\n            if isinstance(field, str):\n                field = self._meta.combined[field]\n            if field.name in field_dict:\n                new_data[field.name] = field_dict[field.name]\n        return new_data\n\n    def _populate_unsaved_relations(self, field_dict):\n        for foreign_key_field in self._meta.refs:\n            foreign_key = foreign_key_field.name\n            conditions = (\n                foreign_key in field_dict and\n                field_dict[foreign_key] is None and\n                self.__rel__.get(foreign_key) is not None)\n            if conditions:\n                setattr(self, foreign_key, getattr(self, foreign_key))\n                field_dict[foreign_key] = self.__data__[foreign_key]\n\n    def save(self, force_insert=False, only=None):\n        field_dict = self.__data__.copy()\n        if self._meta.primary_key is not False:\n            pk_field = self._meta.primary_key\n            pk_value = self._pk\n        else:\n            pk_field = pk_value = None\n        if only is not None:\n            field_dict = self._prune_fields(field_dict, only)\n        elif self._meta.only_save_dirty and not force_insert:\n            field_dict = self._prune_fields(field_dict, self.dirty_fields)\n            if not field_dict:\n                self._dirty.clear()\n                return False\n\n        self._populate_unsaved_relations(field_dict)\n        rows = 1\n\n        if self._meta.auto_increment and pk_value is None:\n            field_dict.pop(pk_field.name, None)\n\n        if pk_value is not None and not force_insert:\n            if self._meta.composite_key:\n                for pk_part_name in pk_field.field_names:\n                    field_dict.pop(pk_part_name, None)\n            else:\n                field_dict.pop(pk_field.name, None)\n            if not field_dict:\n                raise ValueError('no data to save!')\n            rows = self.update(**field_dict).where(self._pk_expr()).execute()\n        elif pk_field is not None:\n            pk = self.insert(**field_dict).execute()\n            if pk is not None and (self._meta.auto_increment or\n                                   pk_value is None):\n                self._pk = pk\n                # Although we set the primary-key, do not mark it as dirty.\n                self._dirty.discard(pk_field.name)\n        else:\n            self.insert(**field_dict).execute()\n\n        self._dirty -= set(field_dict)  # Remove any fields we saved.\n        return rows\n\n    def is_dirty(self):\n        return bool(self._dirty)\n\n    @property\n    def dirty_fields(self):\n        return [f for f in self._meta.sorted_fields if f.name in self._dirty]\n\n    @property\n    def dirty_field_names(self):\n        return [f.name for f in self._meta.sorted_fields\n                if f.name in self._dirty]\n\n    def dependencies(self, search_nullable=True, exclude_null_children=False):\n        model_class = type(self)\n        stack = [(type(self), None)]\n        queries = {}\n        seen = set()\n\n        while stack:\n            klass, query = stack.pop()\n            if klass in seen:\n                continue\n            seen.add(klass)\n            for fk, rel_model in klass._meta.backrefs.items():\n                if rel_model is model_class or query is None:\n                    node = (fk == self.__data__[fk.rel_field.name])\n                else:\n                    node = fk << query\n                subquery = (rel_model.select(rel_model._meta.primary_key)\n                            .where(node))\n                if not fk.null or search_nullable:\n                    queries.setdefault(rel_model, []).append((node, fk))\n                    if fk.null and exclude_null_children:\n                        # Do not process additional children of this node, but\n                        # include it in the list of dependencies.\n                        seen.add(rel_model)\n                    else:\n                        stack.append((rel_model, subquery))\n\n        for m in reversed(sort_models(seen)):\n            for sq, q in queries.get(m, ()):\n                yield sq, q\n\n    def delete_instance(self, recursive=False, delete_nullable=False):\n        if recursive:\n            for query, fk in self.dependencies(exclude_null_children=not delete_nullable):\n                model = fk.model\n                if fk.null and not delete_nullable:\n                    model.update(**{fk.name: None}).where(query).execute()\n                else:\n                    model.delete().where(query).execute()\n        return type(self).delete().where(self._pk_expr()).execute()\n\n    def __hash__(self):\n        return hash((self.__class__, self._pk))\n\n    def __eq__(self, other):\n        return (\n            other.__class__ == self.__class__ and\n            self._pk is not None and\n            self._pk == other._pk)\n\n    def __ne__(self, other):\n        return not self == other\n\n    def __sql__(self, ctx):\n        # NOTE: when comparing a foreign-key field whose related-field is not a\n        # primary-key, then doing an equality test for the foreign-key with a\n        # model instance will return the wrong value; since we would return\n        # the primary key for a given model instance.\n        #\n        # This checks to see if we have a converter in the scope, and that we\n        # are converting a foreign-key expression. If so, we hand the model\n        # instance to the converter rather than blindly grabbing the primary-\n        # key. In the event the provided converter fails to handle the model\n        # instance, then we will return the primary-key.\n        if ctx.state.converter is not None and ctx.state.is_fk_expr:\n            try:\n                return ctx.sql(Value(self, converter=ctx.state.converter))\n            except (TypeError, ValueError):\n                pass\n\n        return ctx.sql(Value(getattr(self, self._meta.primary_key.name),\n                             converter=self._meta.primary_key.db_value))\n\n    @classmethod\n    def bind(cls, database, bind_refs=True, bind_backrefs=True, _exclude=None):\n        is_different = cls._meta.database is not database\n        cls._meta.set_database(database)\n        if bind_refs or bind_backrefs:\n            if _exclude is None:\n                _exclude = set()\n            G = cls._meta.model_graph(refs=bind_refs, backrefs=bind_backrefs)\n            for _, model, is_backref in G:\n                if model not in _exclude:\n                    model._meta.set_database(database)\n                    _exclude.add(model)\n        return is_different\n\n    @classmethod\n    def bind_ctx(cls, database, bind_refs=True, bind_backrefs=True):\n        return _BoundModelsContext((cls,), database, bind_refs, bind_backrefs)\n\n    @classmethod\n    def table_exists(cls):\n        M = cls._meta\n        return cls._schema.database.table_exists(M.table.__name__, M.schema)\n\n    @classmethod\n    def create_table(cls, safe=True, **options):\n        if 'fail_silently' in options:\n            __deprecated__('\"fail_silently\" has been deprecated in favor of '\n                           '\"safe\" for the create_table() method.')\n            safe = options.pop('fail_silently')\n\n        if safe and not cls._schema.database.safe_create_index \\\n           and cls.table_exists():\n            return\n        if cls._meta.temporary:\n            options.setdefault('temporary', cls._meta.temporary)\n        cls._schema.create_all(safe, **options)\n\n    @classmethod\n    def drop_table(cls, safe=True, drop_sequences=True, **options):\n        if safe and not cls._schema.database.safe_drop_index \\\n           and not cls.table_exists():\n            return\n        if cls._meta.temporary:\n            options.setdefault('temporary', cls._meta.temporary)\n        cls._schema.drop_all(safe, drop_sequences, **options)\n\n    @classmethod\n    def truncate_table(cls, **options):\n        cls._schema.truncate_table(**options)\n\n    @classmethod\n    def index(cls, *fields, **kwargs):\n        return ModelIndex(cls, fields, **kwargs)\n\n    @classmethod\n    def add_index(cls, *fields, **kwargs):\n        if len(fields) == 1 and isinstance(fields[0], (SQL, Index)):\n            cls._meta.indexes.append(fields[0])\n        else:\n            cls._meta.indexes.append(ModelIndex(cls, fields, **kwargs))\n\n\nclass ModelAlias(Node):\n    \"\"\"Provide a separate reference to a model in a query.\"\"\"\n    def __init__(self, model, alias=None):\n        self.__dict__['model'] = model\n        self.__dict__['alias'] = alias\n\n    def __getattr__(self, attr):\n        # Hack to work-around the fact that properties or other objects\n        # implementing the descriptor protocol (on the model being aliased),\n        # will not work correctly when we use getattr(). So we explicitly pass\n        # the model alias to the descriptor's getter.\n        for b in (self.model,) + self.model.__bases__:\n            try:\n                obj = b.__dict__[attr]\n                if isinstance(obj, ModelDescriptor):\n                    return obj.__get__(None, self)\n            except KeyError:\n                continue\n\n        model_attr = getattr(self.model, attr)\n        if isinstance(model_attr, Field):\n            self.__dict__[attr] = FieldAlias.create(self, model_attr)\n            return self.__dict__[attr]\n        return model_attr\n\n    def __setattr__(self, attr, value):\n        raise AttributeError('Cannot set attributes on model aliases.')\n\n    def get_field_aliases(self):\n        return [getattr(self, n) for n in self.model._meta.sorted_field_names]\n\n    def select(self, *selection):\n        if not selection:\n            selection = self.get_field_aliases()\n        return ModelSelect(self, selection)\n\n    def __call__(self, **kwargs):\n        return self.model(**kwargs)\n\n    def __sql__(self, ctx):\n        if ctx.scope == SCOPE_VALUES:\n            # Return the quoted table name.\n            return ctx.sql(self.model)\n\n        if self.alias:\n            ctx.alias_manager[self] = self.alias\n\n        if ctx.scope == SCOPE_SOURCE:\n            # Define the table and its alias.\n            return (ctx\n                    .sql(self.model._meta.entity)\n                    .literal(' AS ')\n                    .sql(Entity(ctx.alias_manager[self])))\n        else:\n            # Refer to the table using the alias.\n            return ctx.sql(Entity(ctx.alias_manager[self]))\n\n\nclass FieldAlias(Field):\n    def __init__(self, source, field):\n        self.source = source\n        self.model = source.model\n        self.field = field\n\n    @classmethod\n    def create(cls, source, field):\n        class _FieldAlias(cls, type(field)):\n            pass\n        return _FieldAlias(source, field)\n\n    def clone(self):\n        return FieldAlias(self.source, self.field)\n\n    def adapt(self, value): return self.field.adapt(value)\n    def python_value(self, value): return self.field.python_value(value)\n    def db_value(self, value): return self.field.db_value(value)\n    def __getattr__(self, attr):\n        return self.source if attr == 'model' else getattr(self.field, attr)\n\n    def __sql__(self, ctx):\n        return ctx.sql(Column(self.source, self.field.column_name))\n\n\ndef sort_models(models):\n    models = set(models)\n    seen = set()\n    ordering = []\n    def dfs(model):\n        if model in models and model not in seen:\n            seen.add(model)\n            for foreign_key, rel_model in model._meta.refs.items():\n                # Do not depth-first search deferred foreign-keys as this can\n                # cause tables to be created in the incorrect order.\n                if not foreign_key.deferred:\n                    dfs(rel_model)\n            if model._meta.depends_on:\n                for dependency in model._meta.depends_on:\n                    dfs(dependency)\n            ordering.append(model)\n\n    names = lambda m: (m._meta.name, m._meta.table_name)\n    for m in sorted(models, key=names):\n        dfs(m)\n    return ordering\n\n\nclass _ModelQueryHelper(object):\n    default_row_type = ROW.MODEL\n\n    def __init__(self, *args, **kwargs):\n        super(_ModelQueryHelper, self).__init__(*args, **kwargs)\n        if not self._database:\n            self._database = self.model._meta.database\n\n    @Node.copy\n    def objects(self, constructor=None):\n        self._row_type = ROW.CONSTRUCTOR\n        self._constructor = self.model if constructor is None else constructor\n\n    @Node.copy\n    def models(self):\n        self._row_type = ROW.MODEL\n\n    def _get_cursor_wrapper(self, cursor):\n        row_type = self._row_type or self.default_row_type\n        if row_type == ROW.MODEL:\n            return self._get_model_cursor_wrapper(cursor)\n        elif row_type == ROW.DICT:\n            return ModelDictCursorWrapper(cursor, self.model, self._returning)\n        elif row_type == ROW.TUPLE:\n            return ModelTupleCursorWrapper(cursor, self.model, self._returning)\n        elif row_type == ROW.NAMED_TUPLE:\n            return ModelNamedTupleCursorWrapper(cursor, self.model,\n                                                self._returning)\n        elif row_type == ROW.CONSTRUCTOR:\n            return ModelObjectCursorWrapper(cursor, self.model,\n                                            self._returning, self._constructor)\n        else:\n            raise ValueError('Unrecognized row type: \"%s\".' % row_type)\n\n    def _get_model_cursor_wrapper(self, cursor):\n        return ModelObjectCursorWrapper(cursor, self.model, [], self.model)\n\n\nclass ModelRaw(_ModelQueryHelper, RawQuery):\n    def __init__(self, model, sql, params, **kwargs):\n        self.model = model\n        self._returning = ()\n        super(ModelRaw, self).__init__(sql=sql, params=params, **kwargs)\n\n    def get(self):\n        try:\n            return self.execute()[0]\n        except IndexError:\n            sql, params = self.sql()\n            raise self.model.DoesNotExist('%s instance matching query does '\n                                          'not exist:\\nSQL: %s\\nParams: %s' %\n                                          (self.model, sql, params))\n\n\nclass BaseModelSelect(_ModelQueryHelper):\n    def union_all(self, rhs):\n        return ModelCompoundSelectQuery(self.model, self, 'UNION ALL', rhs)\n    __add__ = union_all\n\n    def union(self, rhs):\n        return ModelCompoundSelectQuery(self.model, self, 'UNION', rhs)\n    __or__ = union\n\n    def intersect(self, rhs):\n        return ModelCompoundSelectQuery(self.model, self, 'INTERSECT', rhs)\n    __and__ = intersect\n\n    def except_(self, rhs):\n        return ModelCompoundSelectQuery(self.model, self, 'EXCEPT', rhs)\n    __sub__ = except_\n\n    def __iter__(self):\n        if not self._cursor_wrapper:\n            self.execute()\n        return iter(self._cursor_wrapper)\n\n    def prefetch(self, *subqueries, **kwargs):\n        return prefetch(self, *subqueries, **kwargs)\n\n    def get(self, database=None):\n        clone = self.paginate(1, 1)\n        clone._cursor_wrapper = None\n        try:\n            return clone.execute(database)[0]\n        except IndexError:\n            sql, params = clone.sql()\n            raise self.model.DoesNotExist('%s instance matching query does '\n                                          'not exist:\\nSQL: %s\\nParams: %s' %\n                                          (clone.model, sql, params))\n\n    def get_or_none(self, database=None):\n        try:\n            return self.get(database=database)\n        except self.model.DoesNotExist:\n            pass\n\n    @Node.copy\n    def group_by(self, *columns):\n        grouping = []\n        for column in columns:\n            if is_model(column):\n                grouping.extend(column._meta.sorted_fields)\n            elif isinstance(column, Table):\n                if not column._columns:\n                    raise ValueError('Cannot pass a table to group_by() that '\n                                     'does not have columns explicitly '\n                                     'declared.')\n                grouping.extend([getattr(column, col_name)\n                                 for col_name in column._columns])\n            else:\n                grouping.append(column)\n        self._group_by = grouping\n\n\nclass ModelCompoundSelectQuery(BaseModelSelect, CompoundSelectQuery):\n    def __init__(self, model, *args, **kwargs):\n        self.model = model\n        super(ModelCompoundSelectQuery, self).__init__(*args, **kwargs)\n\n    def _get_model_cursor_wrapper(self, cursor):\n        return self.lhs._get_model_cursor_wrapper(cursor)\n\n\ndef _normalize_model_select(fields_or_models):\n    fields = []\n    for fm in fields_or_models:\n        if is_model(fm):\n            fields.extend(fm._meta.sorted_fields)\n        elif isinstance(fm, ModelAlias):\n            fields.extend(fm.get_field_aliases())\n        elif isinstance(fm, Table) and fm._columns:\n            fields.extend([getattr(fm, col) for col in fm._columns])\n        else:\n            fields.append(fm)\n    return fields\n\n\nclass ModelSelect(BaseModelSelect, Select):\n    def __init__(self, model, fields_or_models, is_default=False):\n        self.model = self._join_ctx = model\n        self._joins = {}\n        self._is_default = is_default\n        fields = _normalize_model_select(fields_or_models)\n        super(ModelSelect, self).__init__([model], fields)\n\n    def clone(self):\n        clone = super(ModelSelect, self).clone()\n        clone._joins = dict(clone._joins)\n        return clone\n\n    def select(self, *fields_or_models):\n        if fields_or_models or not self._is_default:\n            self._is_default = False\n            fields = _normalize_model_select(fields_or_models)\n            return super(ModelSelect, self).select(*fields)\n        return self\n\n    def select_extend(self, *columns):\n        self._is_default = False\n        fields = _normalize_model_select(columns)\n        return super(ModelSelect, self).select_extend(*fields)\n\n    def switch(self, ctx=None):\n        self._join_ctx = self.model if ctx is None else ctx\n        return self\n\n    def _get_model(self, src):\n        if is_model(src):\n            return src, True\n        elif isinstance(src, Table) and src._model:\n            return src._model, False\n        elif isinstance(src, ModelAlias):\n            return src.model, False\n        elif isinstance(src, ModelSelect):\n            return src.model, False\n        return None, False\n\n    def _normalize_join(self, src, dest, on, attr):\n        # Allow \"on\" expression to have an alias that determines the\n        # destination attribute for the joined data.\n        on_alias = isinstance(on, Alias)\n        if on_alias:\n            attr = attr or on._alias\n            on = on.alias()\n\n        # Obtain references to the source and destination models being joined.\n        src_model, src_is_model = self._get_model(src)\n        dest_model, dest_is_model = self._get_model(dest)\n\n        if src_model and dest_model:\n            self._join_ctx = dest\n            constructor = dest_model\n\n            # In the case where the \"on\" clause is a Column or Field, we will\n            # convert that field into the appropriate predicate expression.\n            if not (src_is_model and dest_is_model) and isinstance(on, Column):\n                if on.source is src:\n                    to_field = src_model._meta.columns[on.name]\n                elif on.source is dest:\n                    to_field = dest_model._meta.columns[on.name]\n                else:\n                    raise AttributeError('\"on\" clause Column %s does not '\n                                         'belong to %s or %s.' %\n                                         (on, src_model, dest_model))\n                on = None\n            elif isinstance(on, Field):\n                to_field = on\n                on = None\n            else:\n                to_field = None\n\n            fk_field, is_backref = self._generate_on_clause(\n                src_model, dest_model, to_field, on)\n\n            if on is None:\n                src_attr = 'name' if src_is_model else 'column_name'\n                dest_attr = 'name' if dest_is_model else 'column_name'\n                if is_backref:\n                    lhs = getattr(dest, getattr(fk_field, dest_attr))\n                    rhs = getattr(src, getattr(fk_field.rel_field, src_attr))\n                else:\n                    lhs = getattr(src, getattr(fk_field, src_attr))\n                    rhs = getattr(dest, getattr(fk_field.rel_field, dest_attr))\n                on = (lhs == rhs)\n\n            if not attr:\n                if fk_field is not None and not is_backref:\n                    attr = fk_field.name\n                else:\n                    attr = dest_model._meta.name\n            elif on_alias and fk_field is not None and \\\n                    attr == fk_field.object_id_name and not is_backref:\n                raise ValueError('Cannot assign join alias to \"%s\", as this '\n                                 'attribute is the object_id_name for the '\n                                 'foreign-key field \"%s\"' % (attr, fk_field))\n\n        elif isinstance(dest, Source):\n            constructor = dict\n            attr = attr or dest._alias\n            if not attr and isinstance(dest, Table):\n                attr = attr or dest.__name__\n\n        return (on, attr, constructor)\n\n    def _generate_on_clause(self, src, dest, to_field=None, on=None):\n        meta = src._meta\n        is_backref = fk_fields = False\n\n        # Get all the foreign keys between source and dest, and determine if\n        # the join is via a back-reference.\n        if dest in meta.model_refs:\n            fk_fields = meta.model_refs[dest]\n        elif dest in meta.model_backrefs:\n            fk_fields = meta.model_backrefs[dest]\n            is_backref = True\n\n        if not fk_fields:\n            if on is not None:\n                return None, False\n            raise ValueError('Unable to find foreign key between %s and %s. '\n                             'Please specify an explicit join condition.' %\n                             (src, dest))\n        elif to_field is not None:\n            # If the foreign-key field was specified explicitly, remove all\n            # other foreign-key fields from the list.\n            target = (to_field.field if isinstance(to_field, FieldAlias)\n                      else to_field)\n            fk_fields = [f for f in fk_fields if (\n                         (f is target) or\n                         (is_backref and f.rel_field is to_field))]\n\n        if len(fk_fields) == 1:\n            return fk_fields[0], is_backref\n\n        if on is None:\n            # If multiple foreign-keys exist, try using the FK whose name\n            # matches that of the related model. If not, raise an error as this\n            # is ambiguous.\n            for fk in fk_fields:\n                if fk.name == dest._meta.name:\n                    return fk, is_backref\n\n            raise ValueError('More than one foreign key between %s and %s.'\n                             ' Please specify which you are joining on.' %\n                             (src, dest))\n\n        # If there are multiple foreign-keys to choose from and the join\n        # predicate is an expression, we'll try to figure out which\n        # foreign-key field we're joining on so that we can assign to the\n        # correct attribute when resolving the model graph.\n        to_field = None\n        if isinstance(on, Expression):\n            lhs, rhs = on.lhs, on.rhs\n            # Coerce to set() so that we force Python to compare using the\n            # object's hash rather than equality test, which returns a\n            # false-positive due to overriding __eq__.\n            fk_set = set(fk_fields)\n\n            if isinstance(lhs, Field):\n                lhs_f = lhs.field if isinstance(lhs, FieldAlias) else lhs\n                if lhs_f in fk_set:\n                    to_field = lhs_f\n            elif isinstance(rhs, Field):\n                rhs_f = rhs.field if isinstance(rhs, FieldAlias) else rhs\n                if rhs_f in fk_set:\n                    to_field = rhs_f\n\n        return to_field, False\n\n    @Node.copy\n    def join(self, dest, join_type=JOIN.INNER, on=None, src=None, attr=None):\n        src = self._join_ctx if src is None else src\n\n        if join_type == JOIN.LATERAL or join_type == JOIN.LEFT_LATERAL:\n            on = True\n        elif join_type != JOIN.CROSS:\n            on, attr, constructor = self._normalize_join(src, dest, on, attr)\n            if attr:\n                self._joins.setdefault(src, [])\n                self._joins[src].append((dest, attr, constructor, join_type))\n        elif on is not None:\n            raise ValueError('Cannot specify on clause with cross join.')\n\n        if not self._from_list:\n            raise ValueError('No sources to join on.')\n\n        item = self._from_list.pop()\n        self._from_list.append(Join(item, dest, join_type, on))\n\n    def left_outer_join(self, dest, on=None, src=None, attr=None):\n        return self.join(dest, JOIN.LEFT_OUTER, on, src, attr)\n\n    def join_from(self, src, dest, join_type=JOIN.INNER, on=None, attr=None):\n        return self.join(dest, join_type, on, src, attr)\n\n    def _get_model_cursor_wrapper(self, cursor):\n        if len(self._from_list) == 1 and not self._joins:\n            return ModelObjectCursorWrapper(cursor, self.model,\n                                            self._returning, self.model)\n        return ModelCursorWrapper(cursor, self.model, self._returning,\n                                  self._from_list, self._joins)\n\n    def ensure_join(self, lm, rm, on=None, **join_kwargs):\n        join_ctx = self._join_ctx\n        for dest, _, constructor, _ in self._joins.get(lm, []):\n            if dest == rm:\n                return self\n        return self.switch(lm).join(rm, on=on, **join_kwargs).switch(join_ctx)\n\n    def convert_dict_to_node(self, qdict):\n        accum = []\n        joins = []\n        fks = (ForeignKeyField, BackrefAccessor)\n        for key, value in sorted(qdict.items()):\n            curr = self.model\n            if '__' in key and key.rsplit('__', 1)[1] in DJANGO_MAP:\n                key, op = key.rsplit('__', 1)\n                op = DJANGO_MAP[op]\n            elif value is None:\n                op = DJANGO_MAP['is']\n            else:\n                op = DJANGO_MAP['eq']\n\n            if '__' not in key:\n                # Handle simplest case. This avoids joining over-eagerly when a\n                # direct FK lookup is all that is required.\n                model_attr = getattr(curr, key)\n            else:\n                for piece in key.split('__'):\n                    for dest, attr, _, _ in self._joins.get(curr, ()):\n                        try: model_attr = getattr(curr, piece, None)\n                        except Exception: pass\n                        if attr == piece or (isinstance(dest, ModelAlias) and\n                                             dest.alias == piece):\n                            curr = dest\n                            break\n                    else:\n                        model_attr = getattr(curr, piece)\n                        if value is not None and isinstance(model_attr, fks):\n                            curr = model_attr.rel_model\n                            joins.append(model_attr)\n            accum.append(op(model_attr, value))\n        return accum, joins\n\n    def filter(self, *args, **kwargs):\n        # normalize args and kwargs into a new expression\n        if args and kwargs:\n            dq_node = (reduce(operator.and_, [a.clone() for a in args]) &\n                       DQ(**kwargs))\n        elif args:\n            dq_node = (reduce(operator.and_, [a.clone() for a in args]) &\n                       ColumnBase())\n        elif kwargs:\n            dq_node = DQ(**kwargs) & ColumnBase()\n        else:\n            return self.clone()\n\n        # dq_node should now be an Expression, lhs = Node(), rhs = ...\n        q = collections.deque([dq_node])\n        dq_joins = []\n        seen_joins = set()\n        while q:\n            curr = q.popleft()\n            if not isinstance(curr, Expression):\n                continue\n            for side, piece in (('lhs', curr.lhs), ('rhs', curr.rhs)):\n                if isinstance(piece, DQ):\n                    query, joins = self.convert_dict_to_node(piece.query)\n                    for join in joins:\n                        if join not in seen_joins:\n                            dq_joins.append(join)\n                            seen_joins.add(join)\n                    expression = reduce(operator.and_, query)\n                    # Apply values from the DQ object.\n                    if piece._negated:\n                        expression = Negated(expression)\n                    #expression._alias = piece._alias\n                    setattr(curr, side, expression)\n                else:\n                    q.append(piece)\n\n        if not args or not kwargs:\n            dq_node = dq_node.lhs\n\n        query = self.clone()\n        for field in dq_joins:\n            if isinstance(field, ForeignKeyField):\n                lm, rm = field.model, field.rel_model\n                field_obj = field\n            elif isinstance(field, BackrefAccessor):\n                lm, rm = field.model, field.rel_model\n                field_obj = field.field\n            query = query.ensure_join(lm, rm, field_obj)\n        return query.where(dq_node)\n\n    def create_table(self, name, safe=True, **meta):\n        return self.model._schema.create_table_as(name, self, safe, **meta)\n\n    def __sql_selection__(self, ctx, is_subquery=False):\n        if self._is_default and is_subquery and len(self._returning) > 1 and \\\n           self.model._meta.primary_key is not False:\n            return ctx.sql(self.model._meta.primary_key)\n\n        return ctx.sql(CommaNodeList(self._returning))\n\n\nclass NoopModelSelect(ModelSelect):\n    def __sql__(self, ctx):\n        return self.model._meta.database.get_noop_select(ctx)\n\n    def _get_cursor_wrapper(self, cursor):\n        return CursorWrapper(cursor)\n\n\nclass _ModelWriteQueryHelper(_ModelQueryHelper):\n    def __init__(self, model, *args, **kwargs):\n        self.model = model\n        super(_ModelWriteQueryHelper, self).__init__(model, *args, **kwargs)\n\n    def returning(self, *returning):\n        accum = []\n        for item in returning:\n            if is_model(item):\n                accum.extend(item._meta.sorted_fields)\n            else:\n                accum.append(item)\n        return super(_ModelWriteQueryHelper, self).returning(*accum)\n\n    def _set_table_alias(self, ctx):\n        table = self.model._meta.table\n        ctx.alias_manager[table] = table.__name__\n\n\nclass ModelUpdate(_ModelWriteQueryHelper, Update):\n    pass\n\n\nclass ModelInsert(_ModelWriteQueryHelper, Insert):\n    default_row_type = ROW.TUPLE\n\n    def __init__(self, *args, **kwargs):\n        super(ModelInsert, self).__init__(*args, **kwargs)\n        if self._returning is None and self.model._meta.database is not None:\n            if self.model._meta.database.returning_clause:\n                self._returning = self.model._meta.get_primary_keys()\n\n    def returning(self, *returning):\n        # By default ModelInsert will yield a `tuple` containing the\n        # primary-key of the newly inserted row. But if we are explicitly\n        # specifying a returning clause and have not set a row type, we will\n        # default to returning model instances instead.\n        if returning and self._row_type is None:\n            self._row_type = ROW.MODEL\n        return super(ModelInsert, self).returning(*returning)\n\n    def get_default_data(self):\n        return self.model._meta.defaults\n\n    def get_default_columns(self):\n        fields = self.model._meta.sorted_fields\n        return fields[1:] if self.model._meta.auto_increment else fields\n\n\nclass ModelDelete(_ModelWriteQueryHelper, Delete):\n    pass\n\n\nclass ManyToManyQuery(ModelSelect):\n    def __init__(self, instance, accessor, rel, *args, **kwargs):\n        self._instance = instance\n        self._accessor = accessor\n        self._src_attr = accessor.src_fk.rel_field.name\n        self._dest_attr = accessor.dest_fk.rel_field.name\n        super(ManyToManyQuery, self).__init__(rel, (rel,), *args, **kwargs)\n\n    def _id_list(self, model_or_id_list):\n        if isinstance(model_or_id_list[0], Model):\n            return [getattr(obj, self._dest_attr) for obj in model_or_id_list]\n        return model_or_id_list\n\n    def add(self, value, clear_existing=False):\n        if clear_existing:\n            self.clear()\n\n        accessor = self._accessor\n        src_id = getattr(self._instance, self._src_attr)\n        if isinstance(value, SelectQuery):\n            query = value.columns(\n                Value(src_id),\n                accessor.dest_fk.rel_field)\n            accessor.through_model.insert_from(\n                fields=[accessor.src_fk, accessor.dest_fk],\n                query=query).execute()\n        else:\n            value = ensure_tuple(value)\n            if not value: return\n\n            inserts = [{\n                accessor.src_fk.name: src_id,\n                accessor.dest_fk.name: rel_id}\n                for rel_id in self._id_list(value)]\n            accessor.through_model.insert_many(inserts).execute()\n\n    def remove(self, value):\n        src_id = getattr(self._instance, self._src_attr)\n        if isinstance(value, SelectQuery):\n            column = getattr(value.model, self._dest_attr)\n            subquery = value.columns(column)\n            return (self._accessor.through_model\n                    .delete()\n                    .where(\n                        (self._accessor.dest_fk << subquery) &\n                        (self._accessor.src_fk == src_id))\n                    .execute())\n        else:\n            value = ensure_tuple(value)\n            if not value:\n                return\n            return (self._accessor.through_model\n                    .delete()\n                    .where(\n                        (self._accessor.dest_fk << self._id_list(value)) &\n                        (self._accessor.src_fk == src_id))\n                    .execute())\n\n    def clear(self):\n        src_id = getattr(self._instance, self._src_attr)\n        return (self._accessor.through_model\n                .delete()\n                .where(self._accessor.src_fk == src_id)\n                .execute())\n\n\ndef safe_python_value(conv_func):\n    def validate(value):\n        try:\n            return conv_func(value)\n        except (TypeError, ValueError):\n            return value\n    return validate\n\n\nclass BaseModelCursorWrapper(DictCursorWrapper):\n    def __init__(self, cursor, model, columns):\n        super(BaseModelCursorWrapper, self).__init__(cursor)\n        self.model = model\n        self.select = columns or []\n\n    def initialize(self):\n        combined = self.model._meta.combined\n        table = self.model._meta.table\n        description = self.cursor.description\n\n        self.ncols = len(self.cursor.description)\n        self.columns = []\n        self.converters = converters = [None] * self.ncols\n        self.fields = fields = [None] * self.ncols\n\n        for idx, description_item in enumerate(description):\n            column = orig_column = description_item[0]\n\n            # Try to clean-up messy column descriptions when people do not\n            # provide an alias. The idea is that we take something like:\n            # SUM(\"t1\".\"price\") -> \"price\") -> price\n            dot_index = column.rfind('.')\n            if dot_index != -1:\n                column = column[dot_index + 1:]\n            column = column.strip('()\"`')\n            self.columns.append(column)\n\n            # Now we'll see what they selected and see if we can improve the\n            # column-name being returned - e.g. by mapping it to the selected\n            # field's name.\n            try:\n                raw_node = self.select[idx]\n            except IndexError:\n                if column in combined:\n                    raw_node = node = combined[column]\n                else:\n                    continue\n            else:\n                node = raw_node.unwrap()\n\n            # If this column was given an alias, then we will use whatever\n            # alias was returned by the cursor.\n            is_alias = raw_node.is_alias()\n            if is_alias:\n                self.columns[idx] = orig_column\n\n            # Heuristics used to attempt to get the field associated with a\n            # given SELECT column, so that we can accurately convert the value\n            # returned by the database-cursor into a Python object.\n            if isinstance(node, Field):\n                if raw_node._coerce:\n                    converters[idx] = node.python_value\n                fields[idx] = node\n                if not is_alias:\n                    self.columns[idx] = node.name\n            elif isinstance(node, ColumnBase) and raw_node._converter:\n                converters[idx] = raw_node._converter\n            elif isinstance(node, Function) and node._coerce:\n                if node._python_value is not None:\n                    converters[idx] = node._python_value\n                elif node.arguments and isinstance(node.arguments[0], Node):\n                    # If the first argument is a field or references a column\n                    # on a Model, try using that field's conversion function.\n                    # This usually works, but we use \"safe_python_value()\" so\n                    # that if a TypeError or ValueError occurs during\n                    # conversion we can just fall-back to the raw cursor value.\n                    first = node.arguments[0].unwrap()\n                    if isinstance(first, Entity):\n                        path = first._path[-1]  # Try to look-up by name.\n                        first = combined.get(path)\n                    if isinstance(first, Field):\n                        converters[idx] = safe_python_value(first.python_value)\n            elif column in combined:\n                if node._coerce:\n                    converters[idx] = combined[column].python_value\n                if isinstance(node, Column) and node.source == table:\n                    fields[idx] = combined[column]\n\n        self.no_convert = []\n        self.convert = []\n        for i in range(self.ncols):\n            if converters[i] is not None:\n                self.convert.append(i)\n            else:\n                self.no_convert.append(i)\n\n    def process_row(self, row):\n        raise NotImplementedError\n\n\nclass ModelDictCursorWrapper(BaseModelCursorWrapper):\n    def initialize(self):\n        super(ModelDictCursorWrapper, self).initialize()\n        self.unique_columns = self.dedupe_columns(\n            self.columns,\n            valid_identifiers=False)\n\n    def process_row(self, row):\n        result = {}\n        columns = self.unique_columns\n        for i in self.no_convert:\n            result[columns[i]] = row[i]\n        for i in self.convert:\n            result[columns[i]] = self.converters[i](row[i])\n        return result\n\n\nclass ModelTupleCursorWrapper(BaseModelCursorWrapper):\n    constructor = tuple\n\n    def process_row(self, row):\n        converters = self.converters\n        return self.constructor([\n            (converters[i](row[i]) if converters[i] is not None else row[i])\n            for i in range(self.ncols)])\n\n\nclass ModelNamedTupleCursorWrapper(ModelTupleCursorWrapper):\n    def initialize(self):\n        super(ModelNamedTupleCursorWrapper, self).initialize()\n        identifiers = self.dedupe_columns(self.columns)\n        self.impl = collections.namedtuple('Row', identifiers)\n        self.constructor = lambda row: self.impl(*row)\n\n\nclass ModelObjectCursorWrapper(ModelDictCursorWrapper):\n    def __init__(self, cursor, model, select, constructor):\n        self.constructor = constructor\n        self.is_model = is_model(constructor)\n        super(ModelObjectCursorWrapper, self).__init__(cursor, model, select)\n\n    def initialize(self):\n        super(ModelObjectCursorWrapper, self).initialize()\n        self.identifiers = self.dedupe_columns(self.columns)\n\n    def process_row(self, row):\n        result = {}\n        columns = self.identifiers\n        for i in self.no_convert:\n            result[columns[i]] = row[i]\n        for i in self.convert:\n            result[columns[i]] = self.converters[i](row[i])\n\n        if self.is_model:\n            # Clear out any dirty fields before returning to the user.\n            obj = self.constructor(__no_default__=1, **result)\n            obj._dirty.clear()\n            return obj\n        else:\n            return self.constructor(**result)\n\n\nclass ModelCursorWrapper(BaseModelCursorWrapper):\n    def __init__(self, cursor, model, select, from_list, joins, dicts=False):\n        super(ModelCursorWrapper, self).__init__(cursor, model, select)\n        self.from_list = from_list\n        self.joins = joins\n        self.dicts = dicts\n\n    def initialize(self):\n        super(ModelCursorWrapper, self).initialize()\n        selected_src = set([field.model for field in self.fields\n                            if field is not None])\n        select = self.select\n        columns = [make_identifier(c) for c in self.columns]\n\n        if self.dicts:\n            self.key_to_constructor = {self.model: (dict, False)}\n        else:\n            self.key_to_constructor = {self.model: (self.model, True)}\n        self.src_is_dest = {}\n        self.src_to_dest = []\n        accum = collections.deque(self.from_list)\n        dests = set()\n\n        while accum:\n            curr = accum.popleft()\n            if isinstance(curr, Join):\n                accum.append(curr.lhs)\n                accum.append(curr.rhs)\n                continue\n\n            if curr not in self.joins:\n                continue\n\n            is_dict = isinstance(curr, dict)\n            for key, attr, constructor, join_type in self.joins[curr]:\n                if self.dicts:\n                    constructor = dict\n\n                if key not in self.key_to_constructor:\n                    self.key_to_constructor[key] = (constructor,\n                                                    is_model(constructor))\n\n                    # (src, attr, dest, is_dict, join_type, is outer?).\n                    self.src_to_dest.append((\n                        curr,\n                        attr,\n                        key,\n                        is_dict or self.dicts,\n                        join_type,\n                        join_type.endswith('OUTER')))\n\n                    dests.add(key)\n                    accum.append(key)\n\n        # Ensure that we accommodate everything selected.\n        for src in selected_src:\n            if src not in self.key_to_constructor:\n                if is_model(src):\n                    self.key_to_constructor[src] = (src, True)\n                elif isinstance(src, ModelAlias):\n                    self.key_to_constructor[src] = (src.model, True)\n\n        # Indicate which sources are also dests.\n        for src, _, dest, _, _, _ in self.src_to_dest:\n            self.src_is_dest[src] = src in dests and (dest in selected_src\n                                                      or src in selected_src)\n\n        self.column_keys = []\n        for idx, node in enumerate(select):\n            key = self.model\n            field = self.fields[idx]\n            if field is not None:\n                if isinstance(field, FieldAlias):\n                    key = field.source\n                else:\n                    key = field.model\n            elif isinstance(node, BindTo):\n                if node.dest not in self.key_to_constructor:\n                    raise ValueError('%s specifies bind-to %s, but %s is not '\n                                     'among the selected sources.' %\n                                     (node.unwrap(), node.dest, node.dest))\n                key = node.dest\n            else:\n                if isinstance(node, Node):\n                    node = node.unwrap()\n                if isinstance(node, Column):\n                    key = node.source\n\n            self.column_keys.append(key)\n\n        # Pre-compute flat list of key/col/converter for each column index.\n        self._row_spec = tuple(\n            (i, self.column_keys[i], columns[i], self.converters[i])\n            for i in range(self.ncols))\n\n        # Flatten list of key / constructor / is model? flag.\n        self._constructor_list = [\n            (key, construct, _is_model)\n            for key, (construct, _is_model) in self.key_to_constructor.items()]\n\n        # Pre-compute join-graph reachability.\n        self._dest_reachable = {}\n        for (src, attr, dest, is_dict, join_type, _) in self.src_to_dest:\n            if dest not in self.joins:\n                continue\n            reachable = set()\n            q = collections.deque([dest])\n            while q:\n                curr = q.popleft()\n                if curr in self.joins:\n                    for _key, _, _, _ in self.joins[curr]:\n                        reachable.add(_key)\n                        q.append(_key)\n\n            self._dest_reachable[dest] = frozenset(reachable)\n\n    def process_row(self, row):\n        objects = {}\n        model_list = []\n        for key, constructor, _is_model in self._constructor_list:\n            if _is_model:\n                objects[key] = constructor(__no_default__=True)\n                model_list.append(objects[key])\n            else:\n                objects[key] = constructor()\n\n        default_instance = objects[self.model]\n\n        set_keys = set()\n        for idx, key, column, converter in self._row_spec:\n            # Get the instance corresponding to the selected column/value,\n            # falling back to the \"root\" model instance.\n            instance = objects.get(key, default_instance)\n            column = self.columns[idx]\n            value = row[idx]\n            if value is not None:\n                set_keys.add(key)\n            if converter is not None:\n                value = converter(value)\n\n            if isinstance(instance, dict):\n                instance[column] = value\n            else:\n                setattr(instance, column, value)\n\n        # Need to do some analysis on the joins before this.\n        for (src, attr, dest, is_dict, _, is_outer) in self.src_to_dest:\n            instance = objects.get(src)\n            joined_instance = objects.get(dest)\n            if joined_instance is None and dest not in objects:\n                continue\n\n            # Determine if anything further along in the graph is set.\n            assign = False\n            if dest not in set_keys and dest in self._dest_reachable:\n                assign = bool(self._dest_reachable[dest] & set_keys)\n\n            # If no fields were set on the destination instance then do not\n            # assign an \"empty\" instance.\n            if dest not in set_keys and not assign:\n                if is_outer:\n                    joined_instance = None\n                else:\n                    continue\n\n            # If no fields were set on either the source or the destination,\n            # then we have nothing to do here.\n            if src not in set_keys and dest not in set_keys and is_outer:\n                continue\n\n            if is_dict:\n                instance[attr] = joined_instance\n            else:\n                setattr(instance, attr, joined_instance)\n\n        # When instantiating models from a cursor, we clear the dirty fields.\n        for instance in model_list:\n            instance._dirty.clear()\n\n        return objects[self.model]\n\n\nclass PrefetchQuery(collections.namedtuple('_PrefetchQuery', (\n    'query', 'fields', 'is_backref', 'rel_models', 'field_to_name', 'model'))):\n    def __new__(cls, query, fields=None, is_backref=None, rel_models=None,\n                field_to_name=None, model=None):\n        if fields:\n            if is_backref:\n                if rel_models is None:\n                    rel_models = [field.model for field in fields]\n                foreign_key_attrs = [field.rel_field.name for field in fields]\n            else:\n                if rel_models is None:\n                    rel_models = [field.rel_model for field in fields]\n                foreign_key_attrs = [field.name for field in fields]\n            field_to_name = list(zip(fields, foreign_key_attrs))\n        model = query.model\n        return super(PrefetchQuery, cls).__new__(\n            cls, query, fields, is_backref, rel_models, field_to_name, model)\n\n    def populate_instance(self, instance, id_map):\n        if self.is_backref:\n            for field in self.fields:\n                identifier = instance.__data__[field.name]\n                key = (field, identifier)\n                if key in id_map:\n                    setattr(instance, field.name, id_map[key])\n        else:\n            for field, attname in self.field_to_name:\n                identifier = instance.__data__[field.rel_field.name]\n                key = (field, identifier)\n                rel_instances = id_map.get(key, [])\n                for inst in rel_instances:\n                    setattr(inst, attname, instance)\n                    inst._dirty.clear()\n                setattr(instance, field.backref, rel_instances)\n\n    def store_instance(self, instance, id_map):\n        for field, attname in self.field_to_name:\n            identity = field.rel_field.python_value(instance.__data__[attname])\n            key = (field, identity)\n            if self.is_backref:\n                id_map[key] = instance\n            else:\n                id_map.setdefault(key, [])\n                id_map[key].append(instance)\n\n\ndef prefetch_add_subquery(sq, subqueries, prefetch_type):\n    fixed_queries = [PrefetchQuery(sq)]\n    for i, subquery in enumerate(subqueries):\n        if isinstance(subquery, tuple):\n            subquery, target_model = subquery\n        else:\n            target_model = None\n        if not isinstance(subquery, Query) and is_model(subquery) or \\\n           isinstance(subquery, ModelAlias):\n            subquery = subquery.select()\n        subquery_model = subquery.model\n        for j in reversed(range(i + 1)):\n            fks = backrefs = None\n            fixed = fixed_queries[j]\n            last_query = fixed.query\n            last_model = last_obj = fixed.model\n            if isinstance(last_model, ModelAlias):\n                last_model = last_model.model\n            rels = subquery_model._meta.model_refs.get(last_model, [])\n            if rels:\n                fks = [getattr(subquery_model, fk.name) for fk in rels]\n                pks = [getattr(last_obj, fk.rel_field.name) for fk in rels]\n            else:\n                backrefs = subquery_model._meta.model_backrefs.get(last_model)\n            if (fks or backrefs) and ((target_model is last_obj) or\n                                      (target_model is None)):\n                break\n\n        else:\n            tgt_err = ' using %s' % target_model if target_model else ''\n            raise AttributeError('Error: unable to find foreign key for '\n                                 'query: %s%s' % (subquery, tgt_err))\n\n        dest = (target_model,) if target_model else None\n\n        if fks:\n            if prefetch_type == PREFETCH_TYPE.WHERE:\n                expr = reduce(operator.or_, [\n                    (fk << last_query.select(pk))\n                    for (fk, pk) in zip(fks, pks)])\n                subquery = subquery.where(expr)\n            elif prefetch_type == PREFETCH_TYPE.JOIN:\n                expr = []\n                select_pks = set()\n                for fk, pk in zip(fks, pks):\n                    expr.append(getattr(last_query.c, pk.column_name) == fk)\n                    select_pks.add(pk)\n                subquery = subquery.distinct().join(\n                    last_query.select(*select_pks),\n                    on=reduce(operator.or_, expr))\n            fixed_queries.append(PrefetchQuery(subquery, fks, False, dest))\n        elif backrefs:\n            expr = []\n            fields = []\n            for backref in backrefs:\n                rel_field = getattr(subquery_model, backref.rel_field.name)\n                fk_field = getattr(last_obj, backref.name)\n                fields.append((rel_field, fk_field))\n\n            if prefetch_type == PREFETCH_TYPE.WHERE:\n                for rel_field, fk_field in fields:\n                    expr.append(rel_field << last_query.select(fk_field))\n                subquery = subquery.where(reduce(operator.or_, expr))\n            elif prefetch_type == PREFETCH_TYPE.JOIN:\n                select_fks = []\n                for rel_field, fk_field in fields:\n                    select_fks.append(fk_field)\n                    target = getattr(last_query.c, fk_field.column_name)\n                    expr.append(rel_field == target)\n                subquery = subquery.distinct().join(\n                    last_query.select(*select_fks),\n                    on=reduce(operator.or_, expr))\n            fixed_queries.append(PrefetchQuery(subquery, backrefs, True, dest))\n\n    return fixed_queries\n\n\ndef prefetch(sq, *subqueries, **kwargs):\n    if not subqueries:\n        return sq\n    prefetch_type = kwargs.pop('prefetch_type', PREFETCH_TYPE.WHERE)\n    if kwargs:\n        raise ValueError('Unrecognized arguments: %s' % kwargs)\n\n    fixed_queries = prefetch_add_subquery(sq, subqueries, prefetch_type)\n    deps = {}\n    rel_map = {}\n    for pq in reversed(fixed_queries):\n        query_model = pq.model\n        if pq.fields:\n            for rel_model in pq.rel_models:\n                rel_map.setdefault(rel_model, [])\n                rel_map[rel_model].append(pq)\n\n        deps.setdefault(query_model, {})\n        id_map = deps[query_model]\n        has_relations = bool(rel_map.get(query_model))\n\n        for instance in pq.query:\n            if pq.fields:\n                pq.store_instance(instance, id_map)\n            if has_relations:\n                for rel in rel_map[query_model]:\n                    rel.populate_instance(instance, deps[rel.model])\n\n    return list(pq.query)\n"
  },
  {
    "path": "playhouse/README.md",
    "content": "## Playhouse\n\nThe `playhouse` namespace contains numerous extensions to Peewee. These include vendor-specific database extensions, high-level abstractions to simplify working with databases, and tools for low-level database operations and introspection.\n\n### Vendor extensions\n\n* [SQLite extensions](http://docs.peewee-orm.com/en/latest/peewee/sqlite_ext.html)\n    * Full-text search (FTS3/4/5)\n    * BM25 ranking algorithm implemented as SQLite C extension, backported to FTS4\n    * Virtual tables and C extensions\n    * Closure tables\n    * JSON extension support\n    * LSM1 (key/value database) support\n    * BLOB API\n    * Online backup API\n* [APSW extensions](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#apsw): use Peewee with the powerful [APSW](https://github.com/rogerbinns/apsw) SQLite driver.\n* [SQLCipher](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#sqlcipher-ext): encrypted SQLite databases.\n* [SqliteQ](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#sqliteq): dedicated writer thread for multi-threaded SQLite applications. [More info here](http://charlesleifer.com/blog/multi-threaded-sqlite-without-the-operationalerrors/).\n* [Postgresql extensions](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#postgres-ext)\n    * JSON and JSONB\n    * HStore\n    * Arrays\n    * Server-side cursors\n    * Full-text search\n* [MySQL extensions](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#mysql-ext)\n\n### High-level libraries\n\n* [Extra fields](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#extra-fields)\n    * Compressed field\n    * PickleField\n* [Shortcuts / helpers](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#shortcuts)\n    * Model-to-dict serializer\n    * Dict-to-model deserializer\n* [Hybrid attributes](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#hybrid)\n* [Signals](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#signals): pre/post-save, pre/post-delete, pre-init.\n* [Dataset](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#dataset): high-level API for working with databases popuarlized by the [project of the same name](https://dataset.readthedocs.io/).\n* [Key/Value Store](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#kv): key/value store using SQLite. Supports *smart indexing*, for *Pandas*-style queries.\n\n### Database management and framework support\n\n* [pwiz](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#pwiz): generate model code from a pre-existing database.\n* [Schema migrations](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#migrate): modify your schema using high-level APIs. Even supports dropping or renaming columns in SQLite.\n* [Connection pool](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#pool): simple connection pooling.\n* [Reflection](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#reflection): low-level, cross-platform database introspection\n* [Database URLs](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#db-url): use URLs to connect to database\n* [Test utils](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#test-utils): helpers for unit-testing Peewee applications.\n* [Flask utils](http://docs.peewee-orm.com/en/latest/peewee/playhouse.html#flask-utils): paginated object lists, database connection management, and more.\n"
  },
  {
    "path": "playhouse/__init__.py",
    "content": ""
  },
  {
    "path": "playhouse/_sqlite_udf.pyx",
    "content": "# cython: language_level=3\nfrom libc.stdlib cimport free, malloc\nfrom libc.math cimport log, sqrt\n\nfrom difflib import SequenceMatcher\nfrom random import randint\n\n\n# FTS ranking functions.\n\ncdef double *get_weights(int ncol, tuple raw_weights):\n    cdef:\n        int argc = len(raw_weights)\n        int icol\n        double *weights = <double *>malloc(sizeof(double) * ncol)\n\n    for icol in range(ncol):\n        if argc == 0:\n            weights[icol] = 1.0\n        elif icol < argc:\n            weights[icol] = <double>raw_weights[icol]\n        else:\n            weights[icol] = 0.0\n    return weights\n\ndef peewee_rank(py_match_info, *raw_weights):\n    cdef:\n        unsigned int *match_info\n        unsigned int *phrase_info\n        bytes _match_info_buf = bytes(py_match_info)\n        char *match_info_buf = _match_info_buf\n        int nphrase, ncol, icol, iphrase, hits, global_hits\n        int P_O = 0, C_O = 1, X_O = 2\n        double score = 0.0, weight\n        double *weights\n\n    match_info = <unsigned int *>match_info_buf\n    nphrase = match_info[P_O]\n    ncol = match_info[C_O]\n    weights = get_weights(ncol, raw_weights)\n\n    # matchinfo X value corresponds to, for each phrase in the search query, a\n    # list of 3 values for each column in the search table.\n    # So if we have a two-phrase search query and three columns of data, the\n    # following would be the layout:\n    # p0 : c0=[0, 1, 2],   c1=[3, 4, 5],    c2=[6, 7, 8]\n    # p1 : c0=[9, 10, 11], c1=[12, 13, 14], c2=[15, 16, 17]\n    for iphrase in range(nphrase):\n        phrase_info = &match_info[X_O + iphrase * ncol * 3]\n        for icol in range(ncol):\n            weight = weights[icol]\n            if weight == 0:\n                continue\n\n            # The idea is that we count the number of times the phrase appears\n            # in this column of the current row, compared to how many times it\n            # appears in this column across all rows. The ratio of these values\n            # provides a rough way to score based on \"high value\" terms.\n            hits = phrase_info[3 * icol]\n            global_hits = phrase_info[3 * icol + 1]\n            if hits > 0:\n                score += weight * (<double>hits / <double>global_hits)\n\n    free(weights)\n    return -1 * score\n\ndef peewee_lucene(py_match_info, *raw_weights):\n    # Usage: peewee_lucene(matchinfo(table, 'pcnalx'), 1)\n    cdef:\n        unsigned int *match_info\n        bytes _match_info_buf = bytes(py_match_info)\n        char *match_info_buf = _match_info_buf\n        int nphrase, ncol\n        double total_docs, term_frequency\n        double doc_length, docs_with_term, avg_length\n        double idf, weight, rhs, denom\n        double *weights\n        int P_O = 0, C_O = 1, N_O = 2, L_O, X_O\n        int iphrase, icol, x\n        double score = 0.0\n\n    match_info = <unsigned int *>match_info_buf\n    nphrase = match_info[P_O]\n    ncol = match_info[C_O]\n    total_docs = match_info[N_O]\n\n    L_O = 3 + ncol\n    X_O = L_O + ncol\n    weights = get_weights(ncol, raw_weights)\n\n    for iphrase in range(nphrase):\n        for icol in range(ncol):\n            weight = weights[icol]\n            if weight == 0:\n                continue\n            doc_length = match_info[L_O + icol]\n            x = X_O + (3 * (icol + iphrase * ncol))\n            term_frequency = match_info[x]  # f(qi)\n            docs_with_term = match_info[x + 2] or 1. # n(qi)\n            idf = log(total_docs / (docs_with_term + 1.))\n            tf = sqrt(term_frequency)\n            fieldNorms = 1.0 / sqrt(doc_length)\n            score += (idf * tf * fieldNorms)\n\n    free(weights)\n    return -1 * score\n\ndef peewee_bm25(py_match_info, *raw_weights):\n    # Usage: peewee_bm25(matchinfo(table, 'pcnalx'), 1)\n    # where the second parameter is the index of the column and\n    # the 3rd and 4th specify k and b.\n    cdef:\n        unsigned int *match_info\n        bytes _match_info_buf = bytes(py_match_info)\n        char *match_info_buf = _match_info_buf\n        int nphrase, ncol\n        double B = 0.75, K = 1.2\n        double total_docs, term_frequency\n        double doc_length, docs_with_term, avg_length\n        double idf, weight, ratio, num, b_part, denom, pc_score\n        double *weights\n        int P_O = 0, C_O = 1, N_O = 2, A_O = 3, L_O, X_O\n        int iphrase, icol, x\n        double score = 0.0\n\n    match_info = <unsigned int *>match_info_buf\n    # PCNALX = matchinfo format.\n    # P = 1 = phrase count within query.\n    # C = 1 = searchable columns in table.\n    # N = 1 = total rows in table.\n    # A = c = for each column, avg number of tokens\n    # L = c = for each column, length of current row (in tokens)\n    # X = 3 * c * p = for each phrase and table column,\n    # * phrase count within column for current row.\n    # * phrase count within column for all rows.\n    # * total rows for which column contains phrase.\n    nphrase = match_info[P_O]  # n\n    ncol = match_info[C_O]\n    total_docs = match_info[N_O]  # N\n\n    L_O = A_O + ncol\n    X_O = L_O + ncol\n    weights = get_weights(ncol, raw_weights)\n\n    for iphrase in range(nphrase):\n        for icol in range(ncol):\n            weight = weights[icol]\n            if weight == 0:\n                continue\n\n            x = X_O + (3 * (icol + iphrase * ncol))\n            term_frequency = match_info[x]  # f(qi, D)\n            docs_with_term = match_info[x + 2]  # n(qi)\n\n            # log( (N - n(qi) + 0.5) / (n(qi) + 0.5) )\n            idf = log(\n                    (total_docs - docs_with_term + 0.5) /\n                    (docs_with_term + 0.5))\n            if idf <= 0.0:\n                idf = 1e-6\n\n            doc_length = match_info[L_O + icol]  # |D|\n            avg_length = match_info[A_O + icol]  # avgdl\n            if avg_length == 0:\n                avg_length = 1\n            ratio = doc_length / avg_length\n\n            num = term_frequency * (K + 1)\n            b_part = 1 - B + (B * ratio)\n            denom = term_frequency + (K * b_part)\n\n            pc_score = idf * (num / denom)\n            score += (pc_score * weight)\n\n    free(weights)\n    return -1 * score\n\ndef peewee_bm25f(py_match_info, *raw_weights):\n    # Usage: peewee_bm25f(matchinfo(table, 'pcnalx'), 1)\n    # where the second parameter is the index of the column and\n    # the 3rd and 4th specify k and b.\n    cdef:\n        unsigned int *match_info\n        bytes _match_info_buf = bytes(py_match_info)\n        char *match_info_buf = _match_info_buf\n        int nphrase, ncol\n        double B = 0.75, K = 1.2, epsilon\n        double total_docs, term_frequency, docs_with_term\n        double doc_length = 0.0, avg_length = 0.0\n        double idf, weight, ratio, num, b_part, denom, pc_score\n        double *weights\n        int P_O = 0, C_O = 1, N_O = 2, A_O = 3, L_O, X_O\n        int iphrase, icol, x\n        double score = 0.0\n\n    match_info = <unsigned int *>match_info_buf\n    nphrase = match_info[P_O]  # n\n    ncol = match_info[C_O]\n    total_docs = match_info[N_O]  # N\n\n    L_O = A_O + ncol\n    X_O = L_O + ncol\n\n    for icol in range(ncol):\n        avg_length += match_info[A_O + icol]\n        doc_length += match_info[L_O + icol]\n\n    epsilon = 1.0 / (total_docs * avg_length)\n    if avg_length == 0:\n        avg_length = 1\n    ratio = doc_length / avg_length\n    weights = get_weights(ncol, raw_weights)\n\n    for iphrase in range(nphrase):\n        for icol in range(ncol):\n            weight = weights[icol]\n            if weight == 0:\n                continue\n\n            x = X_O + (3 * (icol + iphrase * ncol))\n            term_frequency = match_info[x]  # f(qi, D)\n            docs_with_term = match_info[x + 2]  # n(qi)\n\n            # log( (N - n(qi) + 0.5) / (n(qi) + 0.5) )\n            idf = log(\n                (total_docs - docs_with_term + 0.5) /\n                (docs_with_term + 0.5))\n            idf = epsilon if idf <= 0 else idf\n\n            num = term_frequency * (K + 1)\n            b_part = 1 - B + (B * ratio)\n            denom = term_frequency + (K * b_part)\n\n            pc_score = idf * ((num / denom) + 1.)\n            score += (pc_score * weight)\n\n    free(weights)\n    return -1 * score\n\n\n# String UDF.\ndef damerau_levenshtein_dist(s1, s2):\n    cdef:\n        int i, j, del_cost, add_cost, sub_cost\n        int s1_len = len(s1), s2_len = len(s2)\n        list one_ago, two_ago, current_row\n        list zeroes = [0] * (s2_len + 1)\n\n    current_row = list(range(1, s2_len + 2))\n\n    current_row[-1] = 0\n    one_ago = None\n\n    for i in range(s1_len):\n        two_ago = one_ago\n        one_ago = current_row\n        current_row = list(zeroes)\n        current_row[-1] = i + 1\n        for j in range(s2_len):\n            del_cost = one_ago[j] + 1\n            add_cost = current_row[j - 1] + 1\n            sub_cost = one_ago[j - 1] + (s1[i] != s2[j])\n            current_row[j] = min(del_cost, add_cost, sub_cost)\n\n            # Handle transpositions.\n            if (i > 0 and j > 0 and s1[i] == s2[j - 1]\n                and s1[i-1] == s2[j] and s1[i] != s2[j]):\n                current_row[j] = min(current_row[j], two_ago[j - 2] + 1)\n\n    return current_row[s2_len - 1]\n\n# String UDF.\ndef levenshtein_dist(a, b):\n    cdef:\n        int add, delete, change\n        int i, j\n        int n = len(a), m = len(b)\n        list current, previous\n        list zeroes\n\n    if n > m:\n        a, b = b, a\n        n, m = m, n\n\n    zeroes = [0] * (m + 1)\n\n    current = list(range(n + 1))\n\n    for i in range(1, m + 1):\n        previous = current\n        current = list(zeroes)\n        current[0] = i\n\n        for j in range(1, n + 1):\n            add = previous[j] + 1\n            delete = current[j - 1] + 1\n            change = previous[j - 1]\n            if a[j - 1] != b[i - 1]:\n                change +=1\n            current[j] = min(add, delete, change)\n\n    return current[n]\n\n# String UDF.\ndef str_dist(a, b):\n    cdef:\n        int t = 0\n\n    for i in SequenceMatcher(None, a, b).get_opcodes():\n        if i[0] == 'equal':\n            continue\n        t = t + max(i[4] - i[3], i[2] - i[1])\n    return t\n\n# Math Aggregate.\ncdef class median(object):\n    cdef:\n        int ct\n        list items\n\n    def __init__(self):\n        self.ct = 0\n        self.items = []\n\n    cdef selectKth(self, int k, int s=0, int e=-1):\n        cdef:\n            int idx\n        if e < 0:\n            e = len(self.items)\n        idx = randint(s, e-1)\n        idx = self.partition_k(idx, s, e)\n        if idx > k:\n            return self.selectKth(k, s, idx)\n        elif idx < k:\n            return self.selectKth(k, idx + 1, e)\n        else:\n            return self.items[idx]\n\n    cdef int partition_k(self, int pi, int s, int e):\n        cdef:\n            int i, x\n\n        val = self.items[pi]\n        # Swap pivot w/last item.\n        self.items[e - 1], self.items[pi] = self.items[pi], self.items[e - 1]\n        x = s\n        for i in range(s, e):\n            if self.items[i] < val:\n                self.items[i], self.items[x] = self.items[x], self.items[i]\n                x += 1\n        self.items[x], self.items[e-1] = self.items[e-1], self.items[x]\n        return x\n\n    def step(self, item):\n        self.items.append(item)\n        self.ct += 1\n\n    def finalize(self):\n        if self.ct == 0:\n            return None\n        elif self.ct < 3:\n            return self.items[0]\n        else:\n            return self.selectKth(self.ct // 2)\n"
  },
  {
    "path": "playhouse/apsw_ext.py",
    "content": "\"\"\"\nPeewee integration with APSW, \"another python sqlite wrapper\".\n\nProject page: https://rogerbinns.github.io/apsw/\n\nAPSW is a really neat library that provides a thin wrapper on top of SQLite's\nC interface.\n\nHere are just a few reasons to use APSW, taken from the documentation:\n\n* APSW gives all functionality of SQLite, including virtual tables, virtual\n  file system, blob i/o, backups and file control.\n* Connections can be shared across threads without any additional locking.\n* Transactions are managed explicitly by your code.\n* APSW can handle nested transactions.\n* Unicode is handled correctly.\n* APSW is faster.\n\"\"\"\nimport apsw\nfrom peewee import *\nfrom peewee import __exception_wrapper__\nfrom peewee import BooleanField as _BooleanField\nfrom peewee import DateField as _DateField\nfrom peewee import DateTimeField as _DateTimeField\nfrom peewee import DecimalField as _DecimalField\nfrom peewee import Insert\nfrom peewee import TimeField as _TimeField\nfrom peewee import logger\n\n\nclass APSWDatabase(SqliteDatabase):\n    server_version = tuple(int(i) for i in apsw.sqlitelibversion().split('.'))\n\n    def __init__(self, database, **kwargs):\n        self._modules = {}\n        super(APSWDatabase, self).__init__(database, **kwargs)\n\n    def register_module(self, mod_name, mod_inst):\n        self._modules[mod_name] = mod_inst\n        if not self.is_closed():\n            self.connection().createmodule(mod_name, mod_inst)\n\n    def unregister_module(self, mod_name):\n        del(self._modules[mod_name])\n\n    def _connect(self):\n        conn = apsw.Connection(self.database, **self.connect_params)\n        if self._timeout is not None:\n            conn.setbusytimeout(self._timeout * 1000)\n        try:\n            self._add_conn_hooks(conn)\n        except:\n            conn.close()\n            raise\n        return conn\n\n    def _add_conn_hooks(self, conn):\n        super(APSWDatabase, self)._add_conn_hooks(conn)\n        self._load_modules(conn)  # APSW-only.\n\n    def _load_modules(self, conn):\n        for mod_name, mod_inst in self._modules.items():\n            conn.createmodule(mod_name, mod_inst)\n        return conn\n\n    def _load_aggregates(self, conn):\n        for name, (klass, num_params) in self._aggregates.items():\n            def make_aggregate():\n                return (klass(), klass.step, klass.finalize)\n            conn.createaggregatefunction(name, make_aggregate)\n\n    def _load_collations(self, conn):\n        for name, fn in self._collations.items():\n            conn.createcollation(name, fn)\n\n    def _load_functions(self, conn):\n        for name, (fn, num_params, deterministic) in self._functions.items():\n            args = (deterministic,) if deterministic else ()\n            conn.createscalarfunction(name, fn, num_params, *args)\n\n    def _load_extensions(self, conn):\n        conn.enableloadextension(True)\n        for extension in self._extensions:\n            conn.loadextension(extension)\n\n    def load_extension(self, extension):\n        self._extensions.add(extension)\n        if not self.is_closed():\n            conn = self.connection()\n            conn.enableloadextension(True)\n            conn.loadextension(extension)\n\n    def last_insert_id(self, cursor, query_type=None):\n        if not self.returning_clause:\n            return cursor.connection.last_insert_rowid()\n        elif query_type == Insert.SIMPLE:\n            try:\n                return cursor[0][0]\n            except (AttributeError, IndexError, TypeError):\n                pass\n        return cursor\n\n    def rows_affected(self, cursor):\n        try:\n            return cursor.connection.changes()\n        except AttributeError:\n            return cursor.cursor.connection.changes()  # RETURNING query.\n\n    def begin(self, lock_type='deferred'):\n        self.cursor().execute('begin %s;' % lock_type)\n\n    def commit(self):\n        with __exception_wrapper__:\n            curs = self.cursor()\n            if curs.connection.getautocommit():\n                return False\n            curs.execute('commit;')\n        return True\n\n    def rollback(self):\n        with __exception_wrapper__:\n            curs = self.cursor()\n            if curs.connection.getautocommit():\n                return False\n            curs.execute('rollback;')\n        return True\n\n\ndef nh(s, v):\n    if v is not None:\n        return str(v)\n\nclass BooleanField(_BooleanField):\n    def db_value(self, v):\n        v = super(BooleanField, self).db_value(v)\n        if v is not None:\n            return v and 1 or 0\n\nclass DateField(_DateField):\n    db_value = nh\n\nclass TimeField(_TimeField):\n    db_value = nh\n\nclass DateTimeField(_DateTimeField):\n    db_value = nh\n\nclass DecimalField(_DecimalField):\n    db_value = nh\n"
  },
  {
    "path": "playhouse/cockroachdb.py",
    "content": "import functools\nimport re\nimport sys\n\nfrom peewee import *\nfrom peewee import _atomic\nfrom peewee import _manual\nfrom peewee import ColumnMetadata  # (name, data_type, null, primary_key, table, default)\nfrom peewee import EnclosedNodeList\nfrom peewee import Entity\nfrom peewee import ForeignKeyMetadata  # (column, dest_table, dest_column, table).\nfrom peewee import IndexMetadata\nfrom peewee import NodeList\nfrom playhouse.pool import _PooledPostgresqlDatabase\ntry:\n    from playhouse.postgres_ext import ArrayField\n    from playhouse.postgres_ext import BinaryJSONField\n    from playhouse.postgres_ext import IntervalField\n    JSONField = BinaryJSONField\nexcept ImportError:  # psycopg2 not installed, ignore.\n    ArrayField = BinaryJSONField = IntervalField = JSONField = None\n\n\nNESTED_TX_MIN_VERSION = 200100\n\nTXN_ERR_MSG = ('CockroachDB does not support nested transactions. You may '\n               'alternatively use the @transaction context-manager/decorator, '\n               'which only wraps the outer-most block in transactional logic. '\n               'To run a transaction with automatic retries, use the '\n               'run_transaction() helper.')\n\nclass ExceededMaxAttempts(OperationalError): pass\n\n\nclass UUIDKeyField(UUIDField):\n    auto_increment = True\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get('constraints'):\n            raise ValueError('%s cannot specify constraints.' % type(self))\n        kwargs['constraints'] = [SQL('DEFAULT gen_random_uuid()')]\n        kwargs.setdefault('primary_key', True)\n        super(UUIDKeyField, self).__init__(*args, **kwargs)\n\n\nclass RowIDField(AutoField):\n    field_type = 'INT'\n\n    def __init__(self, *args, **kwargs):\n        if kwargs.get('constraints'):\n            raise ValueError('%s cannot specify constraints.' % type(self))\n        kwargs['constraints'] = [SQL('DEFAULT unique_rowid()')]\n        super(RowIDField, self).__init__(*args, **kwargs)\n\n\nclass CockroachDatabase(PostgresqlDatabase):\n    field_types = PostgresqlDatabase.field_types.copy()\n    field_types.update({\n        'BLOB': 'BYTES',\n    })\n\n    release_after_rollback = True\n\n    def __init__(self, database, *args, **kwargs):\n        # Unless a DSN or database connection-url were specified, provide\n        # convenient defaults for the user and port.\n        if 'dsn' not in kwargs and (database and\n                                    not database.startswith('postgresql://')):\n            kwargs.setdefault('user', 'root')\n            kwargs.setdefault('port', 26257)\n        super(CockroachDatabase, self).__init__(database, *args, **kwargs)\n\n    def _set_server_version(self, conn):\n        curs = conn.cursor()\n        curs.execute('select version()')\n        raw, = curs.fetchone()\n        match_obj = re.match(r'^CockroachDB.+?v(\\d+)\\.(\\d+)\\.(\\d+)', raw)\n        if match_obj is not None:\n            clean = '%d%02d%02d' % tuple(int(i) for i in match_obj.groups())\n            self.server_version = int(clean)  # 19.1.5 -> 190105.\n        else:\n            # Fallback to use whatever cockroachdb tells us via protocol.\n            super(CockroachDatabase, self)._set_server_version(conn)\n\n    def _get_pk_constraint(self, table, schema=None):\n        query = ('SELECT constraint_name '\n                 'FROM information_schema.table_constraints '\n                 'WHERE table_name = %s AND table_schema = %s '\n                 'AND constraint_type = %s')\n        cursor = self.execute_sql(query, (table, schema or 'public',\n                                          'PRIMARY KEY'))\n        row = cursor.fetchone()\n        return row and row[0] or None\n\n    def get_indexes(self, table, schema=None):\n        # The primary-key index is returned by default, so we will just strip\n        # it out here.\n        indexes = super(CockroachDatabase, self).get_indexes(table, schema)\n        pkc = self._get_pk_constraint(table, schema)\n        return [idx for idx in indexes if (not pkc) or (idx.name != pkc)]\n\n    def conflict_statement(self, on_conflict, query):\n        if not on_conflict._action: return\n\n        action = on_conflict._action.lower()\n        if action in ('replace', 'upsert'):\n            return SQL('UPSERT')\n        elif action not in ('ignore', 'nothing', 'update'):\n            raise ValueError('Un-supported action for conflict resolution. '\n                             'CockroachDB supports REPLACE (UPSERT), IGNORE '\n                             'and UPDATE.')\n\n    def conflict_update(self, oc, query):\n        action = oc._action.lower() if oc._action else ''\n        if action in ('ignore', 'nothing'):\n            parts = [SQL('ON CONFLICT')]\n            if oc._conflict_target:\n                parts.append(EnclosedNodeList([\n                    Entity(col) if isinstance(col, str) else col\n                    for col in oc._conflict_target]))\n            parts.append(SQL('DO NOTHING'))\n            return NodeList(parts)\n        elif action in ('replace', 'upsert'):\n            # No special stuff is necessary, this is just indicated by starting\n            # the statement with UPSERT instead of INSERT.\n            return\n        elif oc._conflict_constraint:\n            raise ValueError('CockroachDB does not support the usage of a '\n                             'constraint name. Use the column(s) instead.')\n\n        return super(CockroachDatabase, self).conflict_update(oc, query)\n\n    def extract_date(self, date_part, date_field):\n        return fn.extract(date_part, date_field)\n\n    def from_timestamp(self, date_field):\n        # CRDB does not allow casting a decimal/float to timestamp, so we first\n        # cast to int, then to timestamptz.\n        return date_field.cast('int').cast('timestamptz')\n\n    def begin(self, system_time=None, priority=None):\n        super(CockroachDatabase, self).begin()\n        if system_time is not None:\n            self.cursor().execute('SET TRANSACTION AS OF SYSTEM TIME %s',\n                                  (system_time,))\n        if priority is not None:\n            priority = priority.lower()\n            if priority not in ('low', 'normal', 'high'):\n                raise ValueError('priority must be low, normal or high')\n            self.cursor().execute('SET TRANSACTION PRIORITY %s' % priority)\n\n    def atomic(self, system_time=None, priority=None):\n        if self.is_closed(): self.connect()  # Side-effect, set server version.\n        if self.server_version < NESTED_TX_MIN_VERSION:\n            return _crdb_atomic(self, system_time, priority)\n        return super(CockroachDatabase, self).atomic(system_time, priority)\n\n    def savepoint(self):\n        if self.is_closed(): self.connect()  # Side-effect, set server version.\n        if self.server_version < NESTED_TX_MIN_VERSION:\n            raise NotImplementedError(TXN_ERR_MSG)\n        return super(CockroachDatabase, self).savepoint()\n\n    def retry_transaction(self, max_attempts=None, system_time=None,\n                          priority=None):\n        def deco(cb):\n            @functools.wraps(cb)\n            def new_fn():\n                return run_transaction(self, cb, max_attempts, system_time,\n                                       priority)\n            return new_fn\n        return deco\n\n    def run_transaction(self, cb, max_attempts=None, system_time=None,\n                        priority=None):\n        return run_transaction(self, cb, max_attempts, system_time, priority)\n\n\nclass _crdb_atomic(_atomic):\n    def __enter__(self):\n        if self.db.transaction_depth() > 0:\n            if not isinstance(self.db.top_transaction(), _manual):\n                raise NotImplementedError(TXN_ERR_MSG)\n        return super(_crdb_atomic, self).__enter__()\n\n\ndef run_transaction(db, callback, max_attempts=None, system_time=None,\n                    priority=None):\n    \"\"\"\n    Run transactional SQL in a transaction with automatic retries.\n\n    User-provided `callback`:\n    * Must accept one parameter, the `db` instance representing the connection\n      the transaction is running under.\n    * Must not attempt to commit, rollback or otherwise manage transactions.\n    * May be called more than once.\n    * Should ideally only contain SQL operations.\n\n    Additionally, the database must not have any open transaction at the time\n    this function is called, as CRDB does not support nested transactions.\n    \"\"\"\n    max_attempts = max_attempts or -1\n    with db.atomic(system_time=system_time, priority=priority) as txn:\n        db.execute_sql('SAVEPOINT cockroach_restart')\n        while max_attempts != 0:\n            try:\n                result = callback(db)\n                db.execute_sql('RELEASE SAVEPOINT cockroach_restart')\n                return result\n            except OperationalError as exc:\n                if exc.orig.pgcode == '40001':\n                    max_attempts -= 1\n                    db.execute_sql('ROLLBACK TO SAVEPOINT cockroach_restart')\n                    continue\n                raise\n    raise ExceededMaxAttempts(None, 'unable to commit transaction')\n\n\nclass PooledCockroachDatabase(_PooledPostgresqlDatabase, CockroachDatabase):\n    pass\n"
  },
  {
    "path": "playhouse/cysqlite_ext.py",
    "content": "import logging\nfrom pathlib import Path\n\nfrom peewee import DecimalField\nfrom peewee import ImproperlyConfigured\nfrom peewee import OP\nfrom peewee import SqliteDatabase\nfrom peewee import __exception_wrapper__\nfrom playhouse.pool import _PooledSqliteDatabase\nfrom playhouse.sqlite_ext import (\n    RowIDField,\n    DocIDField,\n    AutoIncrementField,\n    ISODateTimeField,\n    JSONPath,\n    JSONBPath,\n    JSONField,\n    JSONBField,\n    SearchField,\n    VirtualModel,\n    FTSModel,\n    FTS5Model)\nfrom playhouse.sqlite_udf import rank\n\ntry:\n    import cysqlite\nexcept ImportError as exc:\n    raise ImportError('cysqlite is not installed')\n\n\nlogger = logging.getLogger('peewee')\n\n\ndef __status__(flag, return_highwater=False):\n    def getter(self):\n        result = cysqlite.status(flag)\n        return result[1] if return_highwater else result\n    return property(getter)\n\ndef __dbstatus__(flag, return_highwater=False, return_current=False):\n    \"\"\"\n    Expose a sqlite3_dbstatus() call for a particular flag as a property of\n    the Database instance. Unlike sqlite3_status(), the dbstatus properties\n    pertain to the current connection.\n    \"\"\"\n    def getter(self):\n        if self._state.conn is None:\n            raise ImproperlyConfigured('database connection not opened.')\n        result = self._state.conn.status(flag)\n        if return_current:\n            return result[0]\n        return result[1] if return_highwater else result\n    return property(getter)\n\n\nclass TDecimalField(DecimalField):\n    field_type = 'TEXT'\n\n    def get_modifiers(self): pass\n\n    def db_value(self, value):\n        if value is not None:\n            return str(super(DecimalField, self).db_value(value))\n\n\nclass CySqliteDatabase(SqliteDatabase):\n    def __init__(self, database, rank_functions=True, *args, **kwargs):\n        super(CySqliteDatabase, self).__init__(database, *args, **kwargs)\n\n        self._table_functions = []\n        self._commit_hook = None\n        self._rollback_hook = None\n        self._update_hook = None\n        self._authorizer = None\n        self._trace = None\n        self._progress = None\n\n        if rank_functions:\n            self.register_function(cysqlite.rank_bm25, 'fts_bm25')\n            self.register_function(cysqlite.rank_lucene, 'fts_lucene')\n            self.register_function(rank, 'fts_rank')\n\n    def _connect(self):\n        if cysqlite is None:\n            raise ImproperlyConfigured('cysqlite is not installed.')\n        conn = cysqlite.Connection(self.database, timeout=self._timeout,\n                                   extensions=True, **self.connect_params)\n        try:\n            self._add_conn_hooks(conn)\n        except Exception:\n            conn.close()\n            raise\n        return conn\n\n    def _add_conn_hooks(self, conn):\n        if self._commit_hook is not None:\n            conn.commit_hook(self._commit_hook)\n        if self._rollback_hook is not None:\n            conn.rollback_hook(self._rollback_hook)\n        if self._update_hook is not None:\n            conn.update_hook(self._update_hook)\n        if self._authorizer is not None:\n            conn.authorizer(self._authorizer)\n        if self._trace is not None:\n            conn.trace(*self._trace)\n        if self._progress is not None:\n            conn.progress(*self._progress)\n        super(CySqliteDatabase, self)._add_conn_hooks(conn)\n        if self._table_functions:\n            for table_function in self._table_functions:\n                table_function.register(conn)\n\n    def _set_pragmas(self, conn):\n        for pragma, value in self._pragmas:\n            conn.pragma(pragma, value)\n\n    def _attach_databases(self, conn):\n        for name, db in self._attached.items():\n            conn.attach(db, name)\n\n    def _load_aggregates(self, conn):\n        for name, (klass, num_params) in self._aggregates.items():\n            conn.create_aggregate(klass, name, num_params)\n\n    def _load_collations(self, conn):\n        for name, fn in self._collations.items():\n            conn.create_collation(fn, name)\n\n    def _load_functions(self, conn):\n        for name, (fn, num_params, deterministic) in self._functions.items():\n            conn.create_function(fn, name, num_params, deterministic)\n\n    def _load_window_functions(self, conn):\n        for name, (klass, num_params) in self._window_functions.items():\n            conn.create_window_function(klass, name, num_params)\n\n    def register_table_function(self, klass, name=None):\n        if name is not None:\n            klass.name = name\n        self._table_functions.append(klass)\n        if not self.is_closed():\n            klass.register(self.connection())\n\n    def unregister_table_function(self, name):\n        for idx, klass in enumerate(self._table_functions):\n            if klass.name == name:\n                break\n        else:\n            return False\n        self._table_functions.pop(idx)\n        return True\n\n    def table_function(self, name=None):\n        def decorator(klass):\n            self.register_table_function(klass, name)\n            return klass\n        return decorator\n\n    def on_commit(self, fn):\n        self._commit_hook = fn\n        if not self.is_closed():\n            self.connection().commit_hook(fn)\n        return fn\n\n    def on_rollback(self, fn):\n        self._rollback_hook = fn\n        if not self.is_closed():\n            self.connection().rollback_hook(fn)\n        return fn\n\n    def on_update(self, fn):\n        self._update_hook = fn\n        if not self.is_closed():\n            self.connection().update_hook(fn)\n        return fn\n\n    def authorizer(self, fn):\n        self._authorizer = fn\n        if not self.is_closed():\n            self.connection().authorizer(fn)\n        return fn\n\n    def trace(self, fn, mask=2, expand_sql=True):\n        if fn is None:\n            self._trace = None\n        else:\n            self._trace = (fn, mask, expand_sql)\n        if not self.is_closed():\n            args = (None,) if fn is None else self._trace\n            self.connection().trace(*args)\n        return fn\n\n    def slow_query_log(self, threshold_ms=50, logger=None,\n                       level=logging.WARNING, expand_sql=True):\n        log = logging.getLogger(logger or 'peewee.cysqlite_ext')\n        def _trace(event, sid, sql, ns):\n            if not sql:\n                return\n            ms = ns / 1000000\n            if ms >= threshold_ms:\n                log.log(level, 'Slow query %0.1fms: %s', ms, sql)\n\n        self.trace(_trace, cysqlite.SQLITE_TRACE_PROFILE, expand_sql=expand_sql)\n        return True\n\n    def progress(self, fn, n=1):\n        if fn is None:\n            self._progress = None\n        else:\n            self._progress = (fn, mask)\n        if not self.is_closed():\n            args = (None,) if fn is None else self._progress\n            self.connection().progress(*args)\n        return fn\n\n    def begin(self, lock_type='deferred'):\n        with __exception_wrapper__:\n            self.connection().begin(lock_type)\n\n    def commit(self):\n        with __exception_wrapper__:\n            self.connection().commit()\n\n    def rollback(self):\n        with __exception_wrapper__:\n            self.connection().rollback()\n\n    @property\n    def autocommit(self):\n        return self.connection().autocommit()\n\n    def blob_open(self, table, column, rowid, read_only=False, dbname=None):\n        return self.connection().blob_open(table, column, rowid, read_only,\n                                           db_name)\n\n    def backup(self, destination, pages=None, name=None, progress=None,\n               src_name=None):\n\n        if isinstance(destination, CySqliteDatabase):\n            conn = destination.connection()\n        elif isinstance(destination, cysqlite.Connection):\n            conn = destination\n        elif isinstance(destination, (str, Path)):\n            return self.backup_to_file(str(destination), pages, name,\n                                       progress, src_name)\n\n        return self.connection().backup(conn, pages, name, progress, src_name)\n\n    def backup_to_file(self, filename, pages=None, name=None, progress=None,\n                       src_name=None):\n        return self.connection().backup_to_file(filename, pages, name,\n                                                progress, src_name)\n\n    # Status properties.\n    memory_used = __status__(cysqlite.SQLITE_STATUS_MEMORY_USED)\n    malloc_size = __status__(cysqlite.SQLITE_STATUS_MALLOC_SIZE, True)\n    malloc_count = __status__(cysqlite.SQLITE_STATUS_MALLOC_COUNT)\n    pagecache_used = __status__(cysqlite.SQLITE_STATUS_PAGECACHE_USED)\n    pagecache_overflow = __status__(\n        cysqlite.SQLITE_STATUS_PAGECACHE_OVERFLOW)\n    pagecache_size = __status__(cysqlite.SQLITE_STATUS_PAGECACHE_SIZE, True)\n    scratch_used = __status__(cysqlite.SQLITE_STATUS_SCRATCH_USED)\n    scratch_overflow = __status__(cysqlite.SQLITE_STATUS_SCRATCH_OVERFLOW)\n    scratch_size = __status__(cysqlite.SQLITE_STATUS_SCRATCH_SIZE, True)\n\n    # Connection status properties.\n    lookaside_used = __dbstatus__(cysqlite.SQLITE_DBSTATUS_LOOKASIDE_USED)\n    lookaside_hit = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_LOOKASIDE_HIT, True)\n    lookaside_miss = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, True)\n    lookaside_miss_full = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL, True)\n    cache_used = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_CACHE_USED, False, True)\n    schema_used = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_SCHEMA_USED, False, True)\n    statement_used = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_STMT_USED, False, True)\n    cache_hit = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_CACHE_HIT, False, True)\n    cache_miss = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_CACHE_MISS, False, True)\n    cache_write = __dbstatus__(\n        cysqlite.SQLITE_DBSTATUS_CACHE_WRITE, False, True)\n\n\nclass PooledCySqliteDatabase(_PooledSqliteDatabase, CySqliteDatabase):\n    pass\n\n\nOP.MATCH = 'MATCH'\n\ndef _sqlite_regexp(regex, value):\n    return re.search(regex, value) is not None\n"
  },
  {
    "path": "playhouse/dataset.py",
    "content": "import base64\nimport csv\nimport datetime\nimport json\nimport operator\nimport uuid\nfrom decimal import Decimal\nfrom functools import reduce\nfrom urllib.parse import urlparse\n\nfrom peewee import *\nfrom playhouse.db_url import connect\nfrom playhouse.migrate import migrate\nfrom playhouse.migrate import SchemaMigrator\nfrom playhouse.reflection import Introspector\n\n\nclass DataSet(object):\n    def __init__(self, url, include_views=False, **kwargs):\n        if isinstance(url, Database):\n            self._url = None\n            self._database = url\n            self._database_path = self._database.database\n        else:\n            self._url = url\n            parse_result = urlparse(url)\n            self._database_path = parse_result.path[1:]\n\n            # Connect to the database.\n            self._database = connect(url)\n\n        # Open a connection if one does not already exist.\n        self._database.connect(reuse_if_open=True)\n\n        # Introspect the database and generate models.\n        self._introspector = Introspector.from_database(self._database)\n        self._include_views = include_views\n        self._models = self._introspector.generate_models(\n            skip_invalid=True,\n            literal_column_names=True,\n            include_views=self._include_views,\n            **kwargs)\n        self._migrator = SchemaMigrator.from_database(self._database)\n\n        class BaseModel(Model):\n            class Meta:\n                database = self._database\n        self._base_model = BaseModel\n        self._export_formats = self.get_export_formats()\n        self._import_formats = self.get_import_formats()\n\n    def __repr__(self):\n        return '<DataSet: %s>' % self._database_path\n\n    def get_export_formats(self):\n        return {\n            'csv': CSVExporter,\n            'json': JSONExporter,\n            'tsv': TSVExporter}\n\n    def get_import_formats(self):\n        return {\n            'csv': CSVImporter,\n            'json': JSONImporter,\n            'tsv': TSVImporter}\n\n    def __getitem__(self, table):\n        if table not in self._models and table in self.tables:\n            self.update_cache(table)\n        return Table(self, table, self._models.get(table))\n\n    @property\n    def tables(self):\n        tables = self._database.get_tables()\n        if self._include_views:\n            tables += self.views\n        return tables\n\n    @property\n    def views(self):\n        return [v.name for v in self._database.get_views()]\n\n    def __contains__(self, table):\n        return table in self.tables\n\n    def connect(self, reuse_if_open=False):\n        self._database.connect(reuse_if_open=reuse_if_open)\n\n    def close(self):\n        self._database.close()\n\n    def update_cache(self, table=None):\n        if table:\n            dependencies = [table]\n            if table in self._models:\n                model_class = self._models[table]\n                dependencies.extend([\n                    related._meta.table_name for _, related, _ in\n                    model_class._meta.model_graph()])\n            else:\n                dependencies.extend(self.get_table_dependencies(table))\n        else:\n            dependencies = None  # Update all tables.\n            self._models = {}\n        updated = self._introspector.generate_models(\n            skip_invalid=True,\n            table_names=dependencies,\n            literal_column_names=True,\n            include_views=self._include_views)\n        self._models.update(updated)\n\n    def get_table_dependencies(self, table):\n        stack = [table]\n        accum = []\n        seen = set()\n        while stack:\n            table = stack.pop()\n            for fk_meta in self._database.get_foreign_keys(table):\n                dest = fk_meta.dest_table\n                if dest not in seen:\n                    stack.append(dest)\n                    accum.append(dest)\n        return accum\n\n    def __enter__(self):\n        self.connect()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if not self._database.is_closed():\n            self.close()\n\n    def query(self, sql, params=None):\n        return self._database.execute_sql(sql, params)\n\n    def transaction(self):\n        return self._database.atomic()\n\n    def _check_arguments(self, filename, file_obj, format, format_dict):\n        if filename and file_obj:\n            raise ValueError('file is over-specified. Please use either '\n                             'filename or file_obj, but not both.')\n        if not filename and not file_obj:\n            raise ValueError('A filename or file-like object must be '\n                             'specified.')\n        if format not in format_dict:\n            valid_formats = ', '.join(sorted(format_dict.keys()))\n            raise ValueError('Unsupported format \"%s\". Use one of %s.' % (\n                format, valid_formats))\n\n    def freeze(self, query, format='csv', filename=None, file_obj=None,\n               encoding='utf8', iso8601_datetimes=False, base64_bytes=False,\n               **kwargs):\n        self._check_arguments(filename, file_obj, format, self._export_formats)\n        if filename:\n            file_obj = open(filename, 'w', encoding=encoding)\n\n        exporter = self._export_formats[format](\n            query,\n            iso8601_datetimes=iso8601_datetimes,\n            base64_bytes=base64_bytes)\n\n        exporter.export(file_obj, **kwargs)\n\n        if filename:\n            file_obj.close()\n\n    def thaw(self, table, format='csv', filename=None, file_obj=None,\n             strict=False, encoding='utf8', iso8601_datetimes=False,\n             base64_bytes=False, **kwargs):\n        self._check_arguments(filename, file_obj, format, self._export_formats)\n        if filename:\n            file_obj = open(filename, 'r', encoding=encoding)\n\n        importer = self._import_formats[format](\n            self[table],\n            strict=strict,\n            iso8601_datetimes=iso8601_datetimes,\n            base64_bytes=base64_bytes)\n\n        count = importer.load(file_obj, **kwargs)\n\n        if filename:\n            file_obj.close()\n\n        return count\n\n\nclass Table(object):\n    def __init__(self, dataset, name, model_class):\n        self.dataset = dataset\n        self.name = name\n        if model_class is None:\n            model_class = self._create_model()\n            model_class.create_table()\n            self.dataset._models[name] = model_class\n\n    @property\n    def model_class(self):\n        return self.dataset._models[self.name]\n\n    def __repr__(self):\n        return '<Table: %s>' % self.name\n\n    def __len__(self):\n        return self.find().count()\n\n    def __iter__(self):\n        return iter(self.find().iterator())\n\n    def _create_model(self):\n        class Meta:\n            table_name = self.name\n        return type(\n            str(self.name),\n            (self.dataset._base_model,),\n            {'Meta': Meta})\n\n    def create_index(self, columns, unique=False):\n        index = ModelIndex(self.model_class, columns, unique=unique)\n        self.model_class.add_index(index)\n        self.dataset._database.execute(index)\n\n    def _guess_field_type(self, value):\n        if isinstance(value, str):\n            return TextField\n        if isinstance(value, (datetime.date, datetime.datetime)):\n            return DateTimeField\n        elif value is True or value is False:\n            return BooleanField\n        elif isinstance(value, int):\n            return IntegerField\n        elif isinstance(value, float):\n            return FloatField\n        elif isinstance(value, Decimal):\n            return DecimalField\n        return TextField\n\n    @property\n    def columns(self):\n        return [f.name for f in self.model_class._meta.sorted_fields]\n\n    def _migrate_new_columns(self, data):\n        new_keys = set(data) - set(self.model_class._meta.fields)\n        new_keys -= set(self.model_class._meta.columns)\n        if new_keys:\n            operations = []\n            for key in new_keys:\n                field_class = self._guess_field_type(data[key])\n                field = field_class(null=True)\n                operations.append(\n                    self.dataset._migrator.add_column(self.name, key, field))\n                field.bind(self.model_class, key)\n\n            migrate(*operations)\n\n            self.dataset.update_cache(self.name)\n\n    def __getitem__(self, item):\n        try:\n            return self.model_class[item]\n        except self.model_class.DoesNotExist:\n            pass\n\n    def __setitem__(self, item, value):\n        if not isinstance(value, dict):\n            raise ValueError('Table.__setitem__() value must be a dict')\n\n        pk = self.model_class._meta.primary_key\n        value[pk.name] = item\n\n        try:\n            with self.dataset.transaction() as txn:\n                self.insert(**value)\n        except IntegrityError:\n            self.dataset.update_cache(self.name)\n            self.update(columns=[pk.name], **value)\n\n    def __delitem__(self, item):\n        del self.model_class[item]\n\n    def insert(self, **data):\n        self._migrate_new_columns(data)\n        return self.model_class.insert(**data).execute()\n\n    def _apply_where(self, query, filters, conjunction=None):\n        conjunction = conjunction or operator.and_\n        if filters:\n            expressions = [\n                (self.model_class._meta.fields[column] == value)\n                for column, value in filters.items()]\n            query = query.where(reduce(conjunction, expressions))\n        return query\n\n    def update(self, columns=None, conjunction=None, **data):\n        self._migrate_new_columns(data)\n        filters = {}\n        if columns:\n            for column in columns:\n                filters[column] = data.pop(column)\n\n        return self._apply_where(\n            self.model_class.update(**data),\n            filters,\n            conjunction).execute()\n\n    def _query(self, **query):\n        return self._apply_where(self.model_class.select(), query)\n\n    def find(self, **query):\n        return self._query(**query).dicts()\n\n    def find_one(self, **query):\n        try:\n            return self.find(**query).get()\n        except self.model_class.DoesNotExist:\n            return None\n\n    def all(self):\n        return self.find()\n\n    def delete(self, **query):\n        return self._apply_where(self.model_class.delete(), query).execute()\n\n    def freeze(self, *args, **kwargs):\n        return self.dataset.freeze(self.all(), *args, **kwargs)\n\n    def thaw(self, *args, **kwargs):\n        return self.dataset.thaw(self.name, *args, **kwargs)\n\n\nclass Exporter(object):\n    def __init__(self, query, iso8601_datetimes=False, base64_bytes=False):\n        self.query = query\n        self.iso8601_datetimes = iso8601_datetimes\n        self.base64_bytes = base64_bytes\n\n    def export(self, file_obj):\n        raise NotImplementedError\n\n\n_datetime_types = (datetime.datetime, datetime.date, datetime.time)\n\n\nclass JSONExporter(Exporter):\n    def _make_default(self):\n        def default(o):\n            if isinstance(o, _datetime_types):\n                if self.iso8601_datetimes:\n                    return o.isoformat()\n                else:\n                    return str(o)\n            elif isinstance(o, (Decimal, uuid.UUID)):\n                return str(o)\n            elif isinstance(o, bytes):\n                if self.base64_bytes:\n                    return base64.urlsafe_b64encode(o).decode('utf8')\n                else:\n                    return o.hex()\n            raise TypeError('Unable to serialize %r as JSON' % o)\n\n        return default\n\n    def export(self, file_obj, **kwargs):\n        json.dump(\n            list(self.query),\n            file_obj,\n            default=self._make_default(),\n            **kwargs)\n\n\nclass CSVExporter(Exporter):\n    def export(self, file_obj, header=True, **kwargs):\n        writer = csv.writer(file_obj, **kwargs)\n        tuples = self.query.tuples().execute()\n        tuples.initialize()\n        if header and getattr(tuples, 'columns', None):\n            writer.writerow([column for column in tuples.columns])\n        for row in tuples:\n            accum = []\n            for value in row:\n                if isinstance(value, _datetime_types):\n                    if self.iso8601_datetimes:\n                        value = value.isoformat()\n                    else:\n                        value = str(value)\n                elif isinstance(value, (Decimal, uuid.UUID)):\n                    value = str(value)\n                elif isinstance(value, bytes):\n                    if self.base64_bytes:\n                        value = base64.urlsafe_b64encode(value).decode('utf8')\n                    else:\n                        value = value.hex()\n                accum.append(value)\n\n            writer.writerow(accum)\n\n\nclass TSVExporter(CSVExporter):\n    def export(self, file_obj, header=True, **kwargs):\n        kwargs.setdefault('delimiter', '\\t')\n        return super(TSVExporter, self).export(file_obj, header, **kwargs)\n\n\nclass Importer(object):\n    def __init__(self, table, strict=False, iso8601_datetimes=False,\n                 base64_bytes=False):\n        self.table = table\n        self.strict = strict\n        self.iso8601_datetimes = iso8601_datetimes\n        self.base64_bytes = base64_bytes\n\n        model = self.table.model_class\n        self.columns = model._meta.columns\n        self.columns.update(model._meta.fields)\n\n    def load(self, file_obj):\n        raise NotImplementedError\n\n\nclass JSONImporter(Importer):\n    def load(self, file_obj, **kwargs):\n        data = json.load(file_obj, **kwargs)\n        count = 0\n\n        for row in data:\n            obj = {}\n            for key in row:\n                field = self.columns.get(key)\n                value = row[key]\n                if isinstance(field, DateTimeField) and self.iso8601_datetimes:\n                    value = datetime.datetime.fromisoformat(value)\n                elif isinstance(field, DateField) and self.iso8601_datetimes:\n                    value = datetime.date.fromisoformat(value)\n                elif isinstance(field, BlobField):\n                    if self.base64_bytes:\n                        value = base64.urlsafe_b64decode(value.encode('utf8'))\n                    else:\n                        value = bytes.fromhex(value)\n\n                if field is not None:\n                    value = field.python_value(value)\n                    obj[key] = value\n                elif not self.strict:\n                    obj[key] = value\n\n            if obj:\n                self.table.insert(**obj)\n                count += 1\n\n        return count\n\n\nclass CSVImporter(Importer):\n    def load(self, file_obj, header=True, **kwargs):\n        count = 0\n        reader = csv.reader(file_obj, **kwargs)\n\n        header_fields = []\n        if header:\n            try:\n                header_keys = next(reader)\n            except StopIteration:\n                return count\n\n            for idx, key in enumerate(header_keys):\n                if key in self.columns or not self.strict:\n                    header_fields.append((idx, key, self.columns.get(key)))\n        else:\n            for idx, field in enumerate(self.model._meta.sorted_fields):\n                header_fields.append((idx, field.name, field))\n\n        if not header_fields:\n            return count\n\n        for row in reader:\n            obj = {}\n            for idx, name, field in header_fields:\n                value = row[idx]\n                if field is None:\n                    obj[name] = value\n                    continue\n\n                if isinstance(field, DateTimeField) and self.iso8601_datetimes:\n                    value = datetime.datetime.fromisoformat(value)\n                elif isinstance(field, DateField) and self.iso8601_datetimes:\n                    value = datetime.date.fromisoformat(value)\n                elif isinstance(field, BlobField):\n                    if self.base64_bytes:\n                        value = base64.urlsafe_b64decode(value.encode('utf8'))\n                    else:\n                        value = bytes.fromhex(value)\n\n                obj[field.name] = field.python_value(value)\n\n            self.table.insert(**obj)\n            count += 1\n\n        return count\n\n\nclass TSVImporter(CSVImporter):\n    def load(self, file_obj, header=True, **kwargs):\n        kwargs.setdefault('delimiter', '\\t')\n        return super(TSVImporter, self).load(file_obj, header, **kwargs)\n"
  },
  {
    "path": "playhouse/db_url.py",
    "content": "from urllib.parse import parse_qsl, unquote, urlparse\n\nfrom peewee import *\nfrom playhouse.pool import PooledMySQLDatabase\nfrom playhouse.pool import PooledPostgresqlDatabase\nfrom playhouse.pool import PooledSqliteDatabase\n\n\nschemes = {\n    'mysql': MySQLDatabase,\n    'mysql+pool': PooledMySQLDatabase,\n    'postgres': PostgresqlDatabase,\n    'postgresql': PostgresqlDatabase,\n    'postgres+pool': PooledPostgresqlDatabase,\n    'postgresql+pool': PooledPostgresqlDatabase,\n    'sqlite': SqliteDatabase,\n    'sqlite+pool': PooledSqliteDatabase,\n}\n\ndef register_database(db_class, *names):\n    global schemes\n    for name in names:\n        schemes[name] = db_class\n\ndef parseresult_to_dict(parsed, unquote_password=False, unquote_user=False):\n\n    # urlparse in python 2.6 is broken so query will be empty and instead\n    # appended to path complete with '?'\n    path = parsed.path[1:]  # Ignore leading '/'.\n    query = parsed.query\n\n    connect_kwargs = {'database': path}\n    if parsed.username:\n        connect_kwargs['user'] = parsed.username\n        if unquote_user:\n            connect_kwargs['user'] = unquote(connect_kwargs['user'])\n    if parsed.password:\n        connect_kwargs['password'] = parsed.password\n        if unquote_password:\n            connect_kwargs['password'] = unquote(connect_kwargs['password'])\n    if parsed.hostname:\n        connect_kwargs['host'] = parsed.hostname\n    if parsed.port:\n        connect_kwargs['port'] = parsed.port\n\n    # Adjust parameters for MySQL.\n    if parsed.scheme == 'mysql' and 'password' in connect_kwargs:\n        connect_kwargs['passwd'] = connect_kwargs.pop('password')\n    elif 'sqlite' in parsed.scheme and not connect_kwargs['database']:\n        connect_kwargs['database'] = ':memory:'\n\n    # Get additional connection args from the query string\n    qs_args = parse_qsl(query, keep_blank_values=True)\n    for key, value in qs_args:\n        if value.lower() == 'false':\n            value = False\n        elif value.lower() == 'true':\n            value = True\n        elif value.isdigit():\n            value = int(value)\n        elif '.' in value and all(p.isdigit() for p in value.split('.', 1)):\n            try:\n                value = float(value)\n            except ValueError:\n                pass\n        elif value.lower() in ('null', 'none'):\n            value = None\n\n        connect_kwargs[key] = value\n\n    return connect_kwargs\n\ndef parse(url, unquote_password=False, unquote_user=False):\n    parsed = urlparse(url)\n    return parseresult_to_dict(parsed, unquote_password, unquote_user)\n\ndef connect(url, unquote_password=False, unquote_user=False, **connect_params):\n    parsed = urlparse(url)\n    connect_kwargs = parseresult_to_dict(parsed, unquote_password, unquote_user)\n    connect_kwargs.update(connect_params)\n    database_class = schemes.get(parsed.scheme)\n\n    if database_class is None:\n        if database_class in schemes:\n            raise RuntimeError('Attempted to use \"%s\" but a required library '\n                               'could not be imported.' % parsed.scheme)\n        else:\n            raise RuntimeError('Unrecognized or unsupported scheme: \"%s\".' %\n                               parsed.scheme)\n\n    return database_class(**connect_kwargs)\n\n# Conditionally register additional databases.\n\ntry:\n    from playhouse.apsw_ext import APSWDatabase\n    register_database(APSWDatabase, 'apsw')\nexcept ImportError:\n    pass\n\ntry:\n    from playhouse.cockroachdb import CockroachDatabase\n    from playhouse.cockroachdb import PooledCockroachDatabase\n    register_database(CockroachDatabase, 'cockroachdb', 'crdb')\n    register_database(PooledCockroachDatabase, 'cockroachdb+pool', 'crdb+pool')\nexcept ImportError:\n    pass\n\ntry:\n    from playhouse.cysqlite_ext import CySqliteDatabase\n    from playhouse.cysqlite_ext import PooledCySqliteDatabase\n\n    register_database(CySqliteDatabase, 'cysqlite')\n    register_database(PooledCySqliteDatabase, 'cysqlite+pool')\nexcept ImportError:\n    pass\n\ntry:\n    from playhouse.mysql_ext import MariaDBConnectorDatabase\n    from playhouse.mysql_ext import MySQLConnectorDatabase\n    from playhouse.mysql_ext import PooledMariaDBConnectorDatabase\n    from playhouse.mysql_ext import PooledMySQLConnectorDatabase\n\n    register_database(MariaDBConnectorDatabase, 'mariadbconnector')\n    register_database(MySQLConnectorDatabase, 'mysqlconnector')\n    register_database(PooledMariaDBConnectorDatabase, 'mariadbconnector+pool')\n    register_database(PooledMySQLConnectorDatabase, 'mysqlconnector+pool')\nexcept ImportError:\n    pass\n\ntry:\n    from playhouse.postgres_ext import PooledPostgresqlExtDatabase\n    from playhouse.postgres_ext import PooledPsycopg3Database\n    from playhouse.postgres_ext import PostgresqlExtDatabase\n    from playhouse.postgres_ext import Psycopg3Database\n\n    register_database(\n        PooledPostgresqlExtDatabase,\n        'postgresext+pool', 'postgresqlext+pool')\n    register_database(\n        PostgresqlExtDatabase,\n        'postgresext', 'postgresqlext')\n\n    register_database(PooledPsycopg3Database, 'psycopg3+pool')\n    register_database(Psycopg3Database, 'psycopg3')\nexcept ImportError:\n    pass\n"
  },
  {
    "path": "playhouse/fields.py",
    "content": "import pickle\ntry:\n    import bz2\nexcept ImportError:\n    bz2 = None\ntry:\n    import zlib\nexcept ImportError:\n    zlib = None\n\nfrom peewee import BlobField\n\n\nclass CompressedField(BlobField):\n    ZLIB = 'zlib'\n    BZ2 = 'bz2'\n    algorithm_to_import = {\n        ZLIB: zlib,\n        BZ2: bz2,\n    }\n\n    def __init__(self, compression_level=6, algorithm=ZLIB, *args,\n                 **kwargs):\n        self.compression_level = compression_level\n        if algorithm not in self.algorithm_to_import:\n            raise ValueError('Unrecognized algorithm %s' % algorithm)\n        compress_module = self.algorithm_to_import[algorithm]\n        if compress_module is None:\n            raise ValueError('Missing library required for %s.' % algorithm)\n\n        self.algorithm = algorithm\n        self.compress = compress_module.compress\n        self.decompress = compress_module.decompress\n        super(CompressedField, self).__init__(*args, **kwargs)\n\n    def python_value(self, value):\n        if value is not None:\n            return self.decompress(value)\n\n    def db_value(self, value):\n        if value is not None:\n            return self._constructor(\n                self.compress(value, self.compression_level))\n\n\nclass PickleField(BlobField):\n    def python_value(self, value):\n        if value is not None:\n            return pickle.loads(value)\n\n    def db_value(self, value):\n        if value is not None:\n            pickled = pickle.dumps(value, pickle.HIGHEST_PROTOCOL)\n            return self._constructor(pickled)\n"
  },
  {
    "path": "playhouse/flask_utils.py",
    "content": "import math\nimport sys\n\nfrom flask import abort\nfrom flask import render_template\nfrom flask import request\nfrom peewee import Database\nfrom peewee import DoesNotExist\nfrom peewee import Model\nfrom peewee import Proxy\nfrom peewee import SelectQuery\nfrom playhouse.db_url import connect as db_url_connect\n\n\nclass PaginatedQuery(object):\n    def __init__(self, query_or_model, paginate_by, page_var='page', page=None,\n                 check_bounds=False):\n        self.paginate_by = paginate_by\n        self.page_var = page_var\n        self.page = page or None\n        self.check_bounds = check_bounds\n\n        if isinstance(query_or_model, SelectQuery):\n            self.query = query_or_model\n            self.model = self.query.model\n        else:\n            self.model = query_or_model\n            self.query = self.model.select()\n\n    def get_page(self):\n        if self.page is not None:\n            return self.page\n\n        curr_page = request.args.get(self.page_var)\n        if curr_page and curr_page.isdigit():\n            return max(1, int(curr_page))\n        return 1\n\n    def get_page_count(self):\n        if not hasattr(self, '_page_count'):\n            self._page_count = int(math.ceil(\n                float(self.query.count()) / self.paginate_by))\n        return self._page_count\n\n    def get_object_list(self):\n        if self.check_bounds and self.get_page() > self.get_page_count():\n            abort(404)\n        return self.query.paginate(self.get_page(), self.paginate_by)\n\n    def get_page_range(self, page, total, show=5):\n        # Generate page buttons for a subset of pages, e.g. if the current page\n        # is 4, we have 10 pages, and want to show 5 buttons, this function\n        # returns us: [2, 3, 4, 5, 6]\n        start = max((page - (show // 2)), 1)\n        stop = min(start + show, total) + 1\n        start = max(min(start, stop - show), 1)\n        return list(range(start, stop)[:show])\n\n\ndef get_object_or_404(query_or_model, *query):\n    if not isinstance(query_or_model, SelectQuery):\n        query_or_model = query_or_model.select()\n    try:\n        return query_or_model.where(*query).get()\n    except DoesNotExist:\n        abort(404)\n\ndef object_list(template_name, query, context_variable='object_list',\n                paginate_by=20, page_var='page', page=None, check_bounds=True,\n                **kwargs):\n    paginated_query = PaginatedQuery(\n        query,\n        paginate_by=paginate_by,\n        page_var=page_var,\n        page=page,\n        check_bounds=check_bounds)\n    kwargs[context_variable] = paginated_query.get_object_list()\n    return render_template(\n        template_name,\n        pagination=paginated_query,\n        page=paginated_query.get_page(),\n        **kwargs)\n\ndef get_current_url():\n    if not request.query_string:\n        return request.path\n    return '%s?%s' % (request.path, request.query_string)\n\ndef get_next_url(default='/'):\n    if request.args.get('next'):\n        return request.args['next']\n    elif request.form.get('next'):\n        return request.form['next']\n    return default\n\nclass FlaskDB(object):\n    \"\"\"\n    Convenience wrapper for configuring a Peewee database for use with a Flask\n    application. Provides a base `Model` class and registers handlers to manage\n    the database connection during the request/response cycle.\n\n    Usage::\n\n        from flask import Flask\n        from peewee import *\n        from playhouse.flask_utils import FlaskDB\n\n\n        # The database can be specified using a database URL, or you can pass a\n        # Peewee database instance directly:\n        DATABASE = 'postgresql:///my_app'\n        DATABASE = PostgresqlDatabase('my_app')\n\n        # If we do not want connection-management on any views, we can specify\n        # the view names using FLASKDB_EXCLUDED_ROUTES. The db connection will\n        # not be opened/closed automatically when these views are requested:\n        FLASKDB_EXCLUDED_ROUTES = ('logout',)\n\n        app = Flask(__name__)\n        app.config.from_object(__name__)\n\n        # Now we can configure our FlaskDB:\n        flask_db = FlaskDB(app)\n\n        # Or use the \"deferred initialization\" pattern:\n        flask_db = FlaskDB()\n        flask_db.init_app(app)\n\n        # The `flask_db` provides a base Model-class for easily binding models\n        # to the configured database:\n        class User(flask_db.Model):\n            email = CharField()\n\n    \"\"\"\n    def __init__(self, app=None, database=None, model_class=Model,\n                 excluded_routes=None):\n        self.database = None  # Reference to actual Peewee database instance.\n        self.base_model_class = model_class\n        self._app = app\n        self._db = database  # dict, url, Database, or None (default).\n        self._excluded_routes = excluded_routes or ()\n        if app is not None:\n            self.init_app(app)\n\n    def init_app(self, app):\n        self._app = app\n\n        if self._db is None:\n            if 'DATABASE' in app.config:\n                initial_db = app.config['DATABASE']\n            elif 'DATABASE_URL' in app.config:\n                initial_db = app.config['DATABASE_URL']\n            else:\n                raise ValueError('Missing required configuration data for '\n                                 'database: DATABASE or DATABASE_URL.')\n        else:\n            initial_db = self._db\n\n        if 'FLASKDB_EXCLUDED_ROUTES' in app.config:\n            self._excluded_routes = app.config['FLASKDB_EXCLUDED_ROUTES']\n\n        self._load_database(app, initial_db)\n        self._register_handlers(app)\n\n    def _load_database(self, app, config_value):\n        if isinstance(config_value, Database):\n            database = config_value\n        elif isinstance(config_value, dict):\n            database = self._load_from_config_dict(dict(config_value))\n        else:\n            # Assume a database connection URL.\n            database = db_url_connect(config_value)\n\n        if isinstance(self.database, Proxy):\n            self.database.initialize(database)\n        else:\n            self.database = database\n\n    def _load_from_config_dict(self, config_dict):\n        try:\n            name = config_dict.pop('name')\n            engine = config_dict.pop('engine')\n        except KeyError:\n            raise RuntimeError('DATABASE configuration must specify a '\n                               '`name` and `engine`.')\n\n        if '.' in engine:\n            path, class_name = engine.rsplit('.', 1)\n        else:\n            path, class_name = 'peewee', engine\n\n        try:\n            __import__(path)\n            module = sys.modules[path]\n            database_class = getattr(module, class_name)\n            assert issubclass(database_class, Database)\n        except ImportError:\n            raise RuntimeError('Unable to import %s' % engine)\n        except AttributeError:\n            raise RuntimeError('Database engine not found %s' % engine)\n        except AssertionError:\n            raise RuntimeError('Database engine not a subclass of '\n                               'peewee.Database: %s' % engine)\n\n        return database_class(name, **config_dict)\n\n    def _register_handlers(self, app):\n        app.before_request(self.connect_db)\n        app.teardown_request(self.close_db)\n\n    def get_model_class(self):\n        if self.database is None:\n            raise RuntimeError('Database must be initialized.')\n\n        class BaseModel(self.base_model_class):\n            class Meta:\n                database = self.database\n\n        return BaseModel\n\n    @property\n    def Model(self):\n        if self._app is None:\n            database = getattr(self, 'database', None)\n            if database is None:\n                self.database = Proxy()\n\n        if not hasattr(self, '_model_class'):\n            self._model_class = self.get_model_class()\n        return self._model_class\n\n    def connect_db(self):\n        if self._excluded_routes and request.endpoint in self._excluded_routes:\n            return\n        self.database.connect()\n\n    def close_db(self, exc):\n        if self._excluded_routes and request.endpoint in self._excluded_routes:\n            return\n        if not self.database.is_closed():\n            self.database.close()\n"
  },
  {
    "path": "playhouse/hybrid.py",
    "content": "from peewee import ModelDescriptor\n\n\n# Hybrid methods/attributes, based on similar functionality in SQLAlchemy:\n# http://docs.sqlalchemy.org/en/improve_toc/orm/extensions/hybrid.html\nclass hybrid_method(ModelDescriptor):\n    def __init__(self, func, expr=None):\n        self.func = func\n        self.expr = expr or func\n\n    def __get__(self, instance, instance_type):\n        if instance is None:\n            return self.expr.__get__(instance_type, instance_type.__class__)\n        return self.func.__get__(instance, instance_type)\n\n    def expression(self, expr):\n        self.expr = expr\n        return self\n\n\nclass hybrid_property(ModelDescriptor):\n    def __init__(self, fget, fset=None, fdel=None, expr=None):\n        self.fget = fget\n        self.fset = fset\n        self.fdel = fdel\n        self.expr = expr or fget\n\n    def __get__(self, instance, instance_type):\n        if instance is None:\n            return self.expr(instance_type)\n        return self.fget(instance)\n\n    def __set__(self, instance, value):\n        if self.fset is None:\n            raise AttributeError('Cannot set attribute.')\n        self.fset(instance, value)\n\n    def __delete__(self, instance):\n        if self.fdel is None:\n            raise AttributeError('Cannot delete attribute.')\n        self.fdel(instance)\n\n    def setter(self, fset):\n        self.fset = fset\n        return self\n\n    def deleter(self, fdel):\n        self.fdel = fdel\n        return self\n\n    def expression(self, expr):\n        self.expr = expr\n        return self\n"
  },
  {
    "path": "playhouse/kv.py",
    "content": "import operator\n\nfrom peewee import *\nfrom peewee import sqlite3\nfrom peewee import Expression\nfrom playhouse.fields import PickleField\ntry:\n    from playhouse.cysqlite_ext import CySqliteDatabase as _SqliteDatabase\nexcept ImportError:\n    _SqliteDatabase = SqliteDatabase\n\n\nSentinel = type('Sentinel', (object,), {})\n\n\nclass KeyValue(object):\n    \"\"\"\n    Persistent dictionary.\n\n    :param Field key_field: field to use for key. Defaults to CharField.\n    :param Field value_field: field to use for value. Defaults to PickleField.\n    :param bool ordered: data should be returned in key-sorted order.\n    :param Database database: database where key/value data is stored.\n    :param str table_name: table name for data.\n    \"\"\"\n    def __init__(self, key_field=None, value_field=None, ordered=False,\n                 database=None, table_name='keyvalue'):\n        if key_field is None:\n            key_field = CharField(max_length=255, primary_key=True)\n        if not key_field.primary_key:\n            raise ValueError('key_field must have primary_key=True.')\n\n        if value_field is None:\n            value_field = PickleField()\n\n        self._key_field = key_field\n        self._value_field = value_field\n        self._ordered = ordered\n        self._database = database or _SqliteDatabase(':memory:')\n        self._table_name = table_name\n        support_on_conflict = (isinstance(self._database, PostgresqlDatabase) or\n                              (isinstance(self._database, SqliteDatabase) and\n                               self._database.server_version >= (3, 24)))\n        if support_on_conflict:\n            self.upsert = self._postgres_upsert\n            self.update = self._postgres_update\n        else:\n            self.upsert = self._upsert\n            self.update = self._update\n\n        self.model = self.create_model()\n        self.key = self.model.key\n        self.value = self.model.value\n\n        # Ensure table exists.\n        self.model.create_table()\n\n    def create_model(self):\n        class KeyValue(Model):\n            key = self._key_field\n            value = self._value_field\n            class Meta:\n                database = self._database\n                table_name = self._table_name\n        return KeyValue\n\n    def query(self, *select):\n        query = self.model.select(*select).tuples()\n        if self._ordered:\n            query = query.order_by(self.key)\n        return query\n\n    def convert_expression(self, expr):\n        if not isinstance(expr, Expression):\n            return (self.key == expr), True\n        return expr, False\n\n    def __contains__(self, key):\n        expr, _ = self.convert_expression(key)\n        return self.model.select().where(expr).exists()\n\n    def __len__(self):\n        return len(self.model)\n\n    def __getitem__(self, expr):\n        converted, is_single = self.convert_expression(expr)\n        query = self.query(self.value).where(converted)\n        item_getter = operator.itemgetter(0)\n        result = [item_getter(row) for row in query]\n        if len(result) == 0 and is_single:\n            raise KeyError(expr)\n        elif is_single:\n            return result[0]\n        return result\n\n    def _upsert(self, key, value):\n        (self.model\n         .insert(key=key, value=value)\n         .on_conflict('replace')\n         .execute())\n\n    def _postgres_upsert(self, key, value):\n        (self.model\n         .insert(key=key, value=value)\n         .on_conflict(conflict_target=[self.key],\n                      preserve=[self.value])\n         .execute())\n\n    def __setitem__(self, expr, value):\n        if isinstance(expr, Expression):\n            self.model.update(value=value).where(expr).execute()\n        else:\n            self.upsert(expr, value)\n\n    def __delitem__(self, expr):\n        converted, _ = self.convert_expression(expr)\n        self.model.delete().where(converted).execute()\n\n    def __iter__(self):\n        return iter(self.query().execute())\n\n    def keys(self):\n        return map(operator.itemgetter(0), self.query(self.key))\n\n    def values(self):\n        return map(operator.itemgetter(0), self.query(self.value))\n\n    def items(self):\n        return iter(self.query().execute())\n\n    def _update(self, __data=None, **mapping):\n        if __data is not None:\n            mapping.update(__data)\n        return (self.model\n                .insert_many(list(mapping.items()),\n                             fields=[self.key, self.value])\n                .on_conflict('replace')\n                .execute())\n\n    def _postgres_update(self, __data=None, **mapping):\n        if __data is not None:\n            mapping.update(__data)\n        return (self.model\n                .insert_many(list(mapping.items()),\n                             fields=[self.key, self.value])\n                .on_conflict(conflict_target=[self.key],\n                             preserve=[self.value])\n                .execute())\n\n    def get(self, key, default=None):\n        try:\n            return self[key]\n        except KeyError:\n            return default\n\n    def setdefault(self, key, default=None):\n        try:\n            return self[key]\n        except KeyError:\n            self[key] = default\n            return default\n\n    def pop(self, key, default=Sentinel):\n        with self._database.atomic():\n            try:\n                result = self[key]\n            except KeyError:\n                if default is Sentinel:\n                    raise\n                return default\n            del self[key]\n\n        return result\n\n    def clear(self):\n        self.model.delete().execute()\n"
  },
  {
    "path": "playhouse/migrate.py",
    "content": "\"\"\"\nLightweight schema migrations.\n\nExample Usage\n-------------\n\nInstantiate a migrator:\n\n    # Postgres example:\n    my_db = PostgresqlDatabase(...)\n    migrator = PostgresqlMigrator(my_db)\n\n    # SQLite example:\n    my_db = SqliteDatabase('my_database.db')\n    migrator = SqliteMigrator(my_db)\n\nThen you will use the `migrate` function to run various `Operation`s which\nare generated by the migrator:\n\n    migrate(\n        migrator.add_column('some_table', 'column_name', CharField(default=''))\n    )\n\nMigrations are not run inside a transaction, so if you wish the migration to\nrun in a transaction you will need to wrap the call to `migrate` in a\ntransaction block, e.g.:\n\n    with my_db.transaction():\n        migrate(...)\n\nSupported Operations\n--------------------\n\nAdd new field(s) to an existing model:\n\n    # Create your field instances. For non-null fields you must specify a\n    # default value.\n    pubdate_field = DateTimeField(null=True)\n    comment_field = TextField(default='')\n\n    # Run the migration, specifying the database table, field name and field.\n    migrate(\n        migrator.add_column('comment_tbl', 'pub_date', pubdate_field),\n        migrator.add_column('comment_tbl', 'comment', comment_field),\n    )\n\nRenaming a field:\n\n    # Specify the table, original name of the column, and its new name.\n    migrate(\n        migrator.rename_column('story', 'pub_date', 'publish_date'),\n        migrator.rename_column('story', 'mod_date', 'modified_date'),\n    )\n\nDropping a field:\n\n    migrate(\n        migrator.drop_column('story', 'some_old_field'),\n    )\n\nMaking a field nullable or not nullable:\n\n    # Note that when making a field not null that field must not have any\n    # NULL values present.\n    migrate(\n        # Make `pub_date` allow NULL values.\n        migrator.drop_not_null('story', 'pub_date'),\n\n        # Prevent `modified_date` from containing NULL values.\n        migrator.add_not_null('story', 'modified_date'),\n    )\n\nRenaming a table:\n\n    migrate(\n        migrator.rename_table('story', 'stories_tbl'),\n    )\n\nAdding an index:\n\n    # Specify the table, column names, and whether the index should be\n    # UNIQUE or not.\n    migrate(\n        # Create an index on the `pub_date` column.\n        migrator.add_index('story', ('pub_date',), False),\n\n        # Create a multi-column index on the `pub_date` and `status` fields.\n        migrator.add_index('story', ('pub_date', 'status'), False),\n\n        # Create a unique index on the category and title fields.\n        migrator.add_index('story', ('category_id', 'title'), True),\n    )\n\nDropping an index:\n\n    # Specify the index name.\n    migrate(migrator.drop_index('story', 'story_pub_date_status'))\n\nAdding or dropping table constraints:\n\n.. code-block:: python\n\n    # Add a CHECK() constraint to enforce the price cannot be negative.\n    migrate(migrator.add_constraint(\n        'products',\n        'price_check',\n        Check('price >= 0')))\n\n    # Remove the price check constraint.\n    migrate(migrator.drop_constraint('products', 'price_check'))\n\n    # Add a UNIQUE constraint on the first and last names.\n    migrate(migrator.add_unique('person', 'first_name', 'last_name'))\n\"\"\"\nfrom collections import namedtuple\nimport functools\nimport hashlib\nimport re\n\nfrom peewee import *\nfrom peewee import CommaNodeList\nfrom peewee import EnclosedNodeList\nfrom peewee import Entity\nfrom peewee import Expression\nfrom peewee import Node\nfrom peewee import NodeList\nfrom peewee import OP\nfrom peewee import callable_\nfrom peewee import sort_models\nfrom peewee import sqlite3\nfrom peewee import _truncate_constraint_name\ntry:\n    from playhouse.cockroachdb import CockroachDatabase\nexcept ImportError:\n    CockroachDatabase = None\n\n\nclass Operation(object):\n    \"\"\"Encapsulate a single schema altering operation.\"\"\"\n    def __init__(self, migrator, method, *args, **kwargs):\n        self.migrator = migrator\n        self.method = method\n        self.args = args\n        self.kwargs = kwargs\n\n    def execute(self, node):\n        self.migrator.database.execute(node)\n\n    def _handle_result(self, result):\n        if isinstance(result, (Node, Context)):\n            self.execute(result)\n        elif isinstance(result, Operation):\n            result.run()\n        elif isinstance(result, (list, tuple)):\n            for item in result:\n                self._handle_result(item)\n\n    def run(self):\n        kwargs = self.kwargs.copy()\n        kwargs['with_context'] = True\n        method = getattr(self.migrator, self.method)\n        self._handle_result(method(*self.args, **kwargs))\n\n\ndef operation(fn):\n    @functools.wraps(fn)\n    def inner(self, *args, **kwargs):\n        with_context = kwargs.pop('with_context', False)\n        if with_context:\n            return fn(self, *args, **kwargs)\n        return Operation(self, fn.__name__, *args, **kwargs)\n    return inner\n\n\ndef make_index_name(table_name, columns):\n    index_name = '_'.join((table_name,) + tuple(columns))\n    if len(index_name) > 64:\n        index_hash = hashlib.md5(index_name.encode('utf-8')).hexdigest()\n        index_name = '%s_%s' % (index_name[:51], index_hash[:12])\n    return index_name\n\n\nclass SchemaMigrator(object):\n    explicit_create_foreign_key = False\n    explicit_delete_foreign_key = False\n\n    def __init__(self, database):\n        self.database = database\n\n    def make_context(self):\n        return self.database.get_sql_context()\n\n    @classmethod\n    def from_database(cls, database):\n        if CockroachDatabase and isinstance(database, CockroachDatabase):\n            return CockroachDBMigrator(database)\n        elif isinstance(database, PostgresqlDatabase):\n            return PostgresqlMigrator(database)\n        elif isinstance(database, MySQLDatabase):\n            return MySQLMigrator(database)\n        elif isinstance(database, SqliteDatabase):\n            return SqliteMigrator(database)\n        raise ValueError('Unsupported database: %s' % database)\n\n    @operation\n    def apply_default(self, table, column_name, field):\n        default = field.default\n        if callable_(default):\n            default = default()\n\n        return (self.make_context()\n                .literal('UPDATE ')\n                .sql(Entity(table))\n                .literal(' SET ')\n                .sql(Expression(\n                    Entity(column_name),\n                    OP.EQ,\n                    field.db_value(default),\n                    flat=True)))\n\n    def _alter_table(self, ctx, table):\n        return ctx.literal('ALTER TABLE ').sql(Entity(table))\n\n    def _alter_column(self, ctx, table, column):\n        return (self\n                ._alter_table(ctx, table)\n                .literal(' ALTER COLUMN ')\n                .sql(Entity(column)))\n\n    @operation\n    def alter_add_column(self, table, column_name, field):\n        # Make field null at first.\n        ctx = self.make_context()\n        field_null, field.null = field.null, True\n\n        # Set the field's column-name and name, if it is not set or doesn't\n        # match the new value.\n        if field.column_name != column_name:\n            field.name = field.column_name = column_name\n\n        (self\n         ._alter_table(ctx, table)\n         .literal(' ADD COLUMN ')\n         .sql(field.ddl(ctx)))\n\n        field.null = field_null\n        if isinstance(field, ForeignKeyField):\n            self.add_inline_fk_sql(ctx, field)\n        return ctx\n\n    @operation\n    def add_constraint(self, table, name, constraint):\n        return (self\n                ._alter_table(self.make_context(), table)\n                .literal(' ADD CONSTRAINT ')\n                .sql(Entity(name))\n                .literal(' ')\n                .sql(constraint))\n\n    @operation\n    def add_unique(self, table, *column_names):\n        constraint_name = 'uniq_%s' % '_'.join(column_names)\n        constraint = NodeList((\n            SQL('UNIQUE'),\n            EnclosedNodeList([Entity(column) for column in column_names])))\n        return self.add_constraint(table, constraint_name, constraint)\n\n    @operation\n    def drop_constraint(self, table, name):\n        return (self\n                ._alter_table(self.make_context(), table)\n                .literal(' DROP CONSTRAINT ')\n                .sql(Entity(name)))\n\n    def add_inline_fk_sql(self, ctx, field):\n        ctx = (ctx\n               .literal(' REFERENCES ')\n               .sql(Entity(field.rel_model._meta.table_name))\n               .literal(' ')\n               .sql(EnclosedNodeList((Entity(field.rel_field.column_name),))))\n        if field.on_delete is not None:\n            ctx = ctx.literal(' ON DELETE %s' % field.on_delete)\n        if field.on_update is not None:\n            ctx = ctx.literal(' ON UPDATE %s' % field.on_update)\n        return ctx\n\n    @operation\n    def add_foreign_key_constraint(self, table, column_name, rel, rel_column,\n                                   on_delete=None, on_update=None,\n                                   constraint_name=None):\n        constraint = constraint_name or 'fk_%s_%s_refs_%s' % (table,\n                                                              column_name,\n                                                              rel)\n        ctx = (self\n               .make_context()\n               .literal('ALTER TABLE ')\n               .sql(Entity(table))\n               .literal(' ADD CONSTRAINT ')\n               .sql(Entity(_truncate_constraint_name(constraint)))\n               .literal(' FOREIGN KEY ')\n               .sql(EnclosedNodeList((Entity(column_name),)))\n               .literal(' REFERENCES ')\n               .sql(Entity(rel))\n               .literal(' (')\n               .sql(Entity(rel_column))\n               .literal(')'))\n        if on_delete is not None:\n            ctx = ctx.literal(' ON DELETE %s' % on_delete)\n        if on_update is not None:\n            ctx = ctx.literal(' ON UPDATE %s' % on_update)\n        return ctx\n\n    @operation\n    def add_column(self, table, column_name, field):\n        # Adding a column is complicated by the fact that if there are rows\n        # present and the field is non-null, then we need to first add the\n        # column as a nullable field, then set the value, then add a not null\n        # constraint.\n        if not field.null and field.default is None:\n            raise ValueError('%s is not null but has no default' % column_name)\n\n        is_foreign_key = isinstance(field, ForeignKeyField)\n        if is_foreign_key and not field.rel_field:\n            raise ValueError('Foreign keys must specify a `field`.')\n\n        operations = [self.alter_add_column(table, column_name, field)]\n\n        # In the event the field is *not* nullable, update with the default\n        # value and set not null.\n        if not field.null:\n            operations.extend([\n                self.apply_default(table, column_name, field),\n                self.add_not_null(table, column_name)])\n\n        if is_foreign_key and self.explicit_create_foreign_key:\n            operations.append(\n                self.add_foreign_key_constraint(\n                    table,\n                    column_name,\n                    field.rel_model._meta.table_name,\n                    field.rel_field.column_name,\n                    field.on_delete,\n                    field.on_update))\n\n        if field.index or field.unique:\n            using = getattr(field, 'index_type', None)\n            operations.append(self.add_index(table, (column_name,),\n                                             field.unique, using))\n\n        return operations\n\n    @operation\n    def drop_foreign_key_constraint(self, table, column_name):\n        raise NotImplementedError\n\n    @operation\n    def drop_column(self, table, column_name, cascade=True):\n        ctx = self.make_context()\n        (self._alter_table(ctx, table)\n         .literal(' DROP COLUMN ')\n         .sql(Entity(column_name)))\n\n        if cascade:\n            ctx.literal(' CASCADE')\n\n        fk_columns = [\n            foreign_key.column\n            for foreign_key in self.database.get_foreign_keys(table)]\n        if column_name in fk_columns and self.explicit_delete_foreign_key:\n            return [self.drop_foreign_key_constraint(table, column_name), ctx]\n\n        return ctx\n\n    @operation\n    def rename_column(self, table, old_name, new_name):\n        return (self\n                ._alter_table(self.make_context(), table)\n                .literal(' RENAME COLUMN ')\n                .sql(Entity(old_name))\n                .literal(' TO ')\n                .sql(Entity(new_name)))\n\n    @operation\n    def add_not_null(self, table, column):\n        return (self\n                ._alter_column(self.make_context(), table, column)\n                .literal(' SET NOT NULL'))\n\n    @operation\n    def drop_not_null(self, table, column):\n        return (self\n                ._alter_column(self.make_context(), table, column)\n                .literal(' DROP NOT NULL'))\n\n    @operation\n    def add_column_default(self, table, column, default):\n        if default is None:\n            raise ValueError('`default` must be not None/NULL.')\n        if callable_(default):\n            default = default()\n        # Try to handle SQL functions and string literals, otherwise pass as a\n        # bound value.\n        if isinstance(default, str) and default.endswith((')', \"'\")):\n            default = SQL(default)\n\n        return (self\n                ._alter_table(self.make_context(), table)\n                .literal(' ALTER COLUMN ')\n                .sql(Entity(column))\n                .literal(' SET DEFAULT ')\n                .sql(default))\n\n    @operation\n    def drop_column_default(self, table, column):\n        return (self\n                ._alter_table(self.make_context(), table)\n                .literal(' ALTER COLUMN ')\n                .sql(Entity(column))\n                .literal(' DROP DEFAULT'))\n\n    @operation\n    def alter_column_type(self, table, column, field, cast=None):\n        # ALTER TABLE <table> ALTER COLUMN <column>\n        ctx = self.make_context()\n        ctx = (self\n               ._alter_column(ctx, table, column)\n               .literal(' TYPE ')\n               .sql(field.ddl_datatype(ctx)))\n        if cast is not None:\n            if not isinstance(cast, Node):\n                cast = SQL(cast)\n            ctx = ctx.literal(' USING ').sql(cast)\n        return ctx\n\n    @operation\n    def rename_table(self, old_name, new_name):\n        return (self\n                ._alter_table(self.make_context(), old_name)\n                .literal(' RENAME TO ')\n                .sql(Entity(new_name)))\n\n    @operation\n    def add_index(self, table, columns, unique=False, using=None):\n        ctx = self.make_context()\n        index_name = make_index_name(table, columns)\n        table_obj = Table(table)\n        cols = [getattr(table_obj.c, column) for column in columns]\n        index = Index(index_name, table_obj, cols, unique=unique, using=using)\n        return ctx.sql(index)\n\n    @operation\n    def drop_index(self, table, index_name):\n        return (self\n                .make_context()\n                .literal('DROP INDEX ')\n                .sql(Entity(index_name)))\n\n\nclass PostgresqlMigrator(SchemaMigrator):\n    def _primary_key_columns(self, tbl):\n        query = \"\"\"\n            SELECT pg_attribute.attname\n            FROM pg_index, pg_class, pg_attribute\n            WHERE\n                pg_class.oid = %s::regclass AND\n                indrelid = pg_class.oid AND\n                pg_attribute.attrelid = pg_class.oid AND\n                pg_attribute.attnum = any(pg_index.indkey) AND\n                indisprimary;\n        \"\"\"\n        cursor = self.database.execute_sql(query, (tbl,))\n        return [row[0] for row in cursor.fetchall()]\n\n    @operation\n    def set_search_path(self, schema_name):\n        return (self\n                .make_context()\n                .literal('SET search_path TO ')\n                .sql(Entity(schema_name)))\n\n    @operation\n    def rename_table(self, old_name, new_name):\n        pk_names = self._primary_key_columns(old_name)\n        ParentClass = super(PostgresqlMigrator, self)\n\n        operations = [\n            ParentClass.rename_table(old_name, new_name, with_context=True)]\n\n        if len(pk_names) == 1:\n            # Check for existence of primary key sequence.\n            seq_name = '%s_%s_seq' % (old_name, pk_names[0])\n            query = \"\"\"\n                SELECT 1\n                FROM information_schema.sequences\n                WHERE LOWER(sequence_name) = LOWER(%s)\n            \"\"\"\n            cursor = self.database.execute_sql(query, (seq_name,))\n            if bool(cursor.fetchone()):\n                new_seq_name = '%s_%s_seq' % (new_name, pk_names[0])\n                operations.append(ParentClass.rename_table(\n                    seq_name, new_seq_name))\n\n        return operations\n\n\nclass CockroachDBMigrator(PostgresqlMigrator):\n    explicit_create_foreign_key = True\n\n    def add_inline_fk_sql(self, ctx, field):\n        pass\n\n    @operation\n    def drop_index(self, table, index_name):\n        return (self\n                .make_context()\n                .literal('DROP INDEX ')\n                .sql(Entity(index_name))\n                .literal(' CASCADE'))\n\n\nclass MySQLColumn(namedtuple('_Column', ('name', 'definition', 'null', 'pk',\n                                         'default', 'extra'))):\n    @property\n    def is_pk(self):\n        return self.pk == 'PRI'\n\n    @property\n    def is_unique(self):\n        return self.pk == 'UNI'\n\n    @property\n    def is_null(self):\n        return self.null == 'YES'\n\n    def sql(self, column_name=None, is_null=None):\n        if is_null is None:\n            is_null = self.is_null\n        if column_name is None:\n            column_name = self.name\n        parts = [\n            Entity(column_name),\n            SQL(self.definition)]\n        if self.is_unique:\n            parts.append(SQL('UNIQUE'))\n        if is_null:\n            parts.append(SQL('NULL'))\n        else:\n            parts.append(SQL('NOT NULL'))\n        if self.is_pk:\n            parts.append(SQL('PRIMARY KEY'))\n        if self.extra:\n            parts.append(SQL(self.extra))\n        return NodeList(parts)\n\n\nclass MySQLMigrator(SchemaMigrator):\n    explicit_create_foreign_key = True\n    explicit_delete_foreign_key = True\n\n    def _alter_column(self, ctx, table, column):\n        return (self\n                ._alter_table(ctx, table)\n                .literal(' MODIFY ')\n                .sql(Entity(column)))\n\n    @operation\n    def rename_table(self, old_name, new_name):\n        return (self\n                .make_context()\n                .literal('RENAME TABLE ')\n                .sql(Entity(old_name))\n                .literal(' TO ')\n                .sql(Entity(new_name)))\n\n    def _get_column_definition(self, table, column_name):\n        table_safe = table.replace('`', '``')\n        cursor = self.database.execute_sql('DESCRIBE `%s`;' % table_safe)\n        rows = cursor.fetchall()\n        for row in rows:\n            column = MySQLColumn(*row)\n            if column.name == column_name:\n                return column\n        return False\n\n    def get_foreign_key_constraint(self, table, column_name):\n        cursor = self.database.execute_sql(\n            ('SELECT constraint_name '\n             'FROM information_schema.key_column_usage WHERE '\n             'table_schema = DATABASE() AND '\n             'table_name = %s AND '\n             'column_name = %s AND '\n             'referenced_table_name IS NOT NULL AND '\n             'referenced_column_name IS NOT NULL;'),\n            (table, column_name))\n        result = cursor.fetchone()\n        if not result:\n            raise AttributeError(\n                'Unable to find foreign key constraint for '\n                '\"%s\" on table \"%s\".' % (column_name, table))\n        return result[0]\n\n    @operation\n    def drop_foreign_key_constraint(self, table, column_name):\n        fk_constraint = self.get_foreign_key_constraint(table, column_name)\n        return (self\n                ._alter_table(self.make_context(), table)\n                .literal(' DROP FOREIGN KEY ')\n                .sql(Entity(fk_constraint)))\n\n    def add_inline_fk_sql(self, ctx, field):\n        pass\n\n    @operation\n    def add_not_null(self, table, column):\n        column_def = self._get_column_definition(table, column)\n        add_not_null = (self\n                        ._alter_table(self.make_context(), table)\n                        .literal(' MODIFY ')\n                        .sql(column_def.sql(is_null=False)))\n\n        fk_objects = dict(\n            (fk.column, fk)\n            for fk in self.database.get_foreign_keys(table))\n        if column not in fk_objects:\n            return add_not_null\n\n        fk_metadata = fk_objects[column]\n        return (self.drop_foreign_key_constraint(table, column),\n                add_not_null,\n                self.add_foreign_key_constraint(\n                    table,\n                    column,\n                    fk_metadata.dest_table,\n                    fk_metadata.dest_column))\n\n    @operation\n    def drop_not_null(self, table, column):\n        column_def = self._get_column_definition(table, column)\n        if column_def.is_pk:\n            raise ValueError('Primary keys can not be null')\n        return (self\n                ._alter_table(self.make_context(), table)\n                .literal(' MODIFY ')\n                .sql(column_def.sql(is_null=True)))\n\n    @operation\n    def rename_column(self, table, old_name, new_name):\n        fk_objects = dict(\n            (fk.column, fk)\n            for fk in self.database.get_foreign_keys(table))\n        is_foreign_key = old_name in fk_objects\n\n        column = self._get_column_definition(table, old_name)\n        rename_ctx = (self\n                      ._alter_table(self.make_context(), table)\n                      .literal(' CHANGE ')\n                      .sql(Entity(old_name))\n                      .literal(' ')\n                      .sql(column.sql(column_name=new_name)))\n        if is_foreign_key:\n            fk_metadata = fk_objects[old_name]\n            return [\n                self.drop_foreign_key_constraint(table, old_name),\n                rename_ctx,\n                self.add_foreign_key_constraint(\n                    table,\n                    new_name,\n                    fk_metadata.dest_table,\n                    fk_metadata.dest_column),\n            ]\n        else:\n            return rename_ctx\n\n    @operation\n    def alter_column_type(self, table, column, field, cast=None):\n        if cast is not None:\n            raise ValueError('alter_column_type() does not support cast with '\n                             'MySQL.')\n        ctx = self.make_context()\n        return (self\n                ._alter_table(ctx, table)\n                .literal(' MODIFY ')\n                .sql(Entity(column))\n                .literal(' ')\n                .sql(field.ddl(ctx)))\n\n    @operation\n    def drop_index(self, table, index_name):\n        return (self\n                .make_context()\n                .literal('DROP INDEX ')\n                .sql(Entity(index_name))\n                .literal(' ON ')\n                .sql(Entity(table)))\n\n\nclass SqliteMigrator(SchemaMigrator):\n    \"\"\"\n    SQLite supports a subset of ALTER TABLE queries, view the docs for the\n    full details http://sqlite.org/lang_altertable.html\n    \"\"\"\n    column_re = re.compile(r'(.+?)\\((.+)\\)')\n    column_split_re = re.compile(r'(?:[^,(]|\\([^)]*\\))+')\n    column_name_re = re.compile(r'''[\"`']?([\\w]+)''')\n    fk_re = re.compile(r'FOREIGN KEY\\s+\\(\"?([\\w]+)\"?\\)\\s+', re.I)\n\n    def _get_column_names(self, table):\n        quoted = table.replace('\"', '\"\"')\n        res = self.database.execute_sql('select * from \"%s\" limit 1' % quoted)\n        return [item[0] for item in res.description]\n\n    def _get_create_table(self, table):\n        res = self.database.execute_sql(\n            ('select name, sql from sqlite_master '\n             'where type=? and LOWER(name)=?'),\n            ['table', table.lower()])\n        return res.fetchone()\n\n    @operation\n    def _update_column(self, table, column_to_update, fn):\n        columns = set(column.name.lower()\n                      for column in self.database.get_columns(table))\n        if column_to_update.lower() not in columns:\n            raise ValueError('Column \"%s\" does not exist on \"%s\"' %\n                             (column_to_update, table))\n\n        # Get the SQL used to create the given table.\n        table, create_table = self._get_create_table(table)\n\n        # Get the indexes and SQL to re-create indexes.\n        indexes = self.database.get_indexes(table)\n\n        # Make sure the create_table does not contain any newlines or tabs,\n        # allowing the regex to work correctly.\n        create_table = re.sub(r'\\s+', ' ', create_table)\n\n        # Parse out the `CREATE TABLE` and column list portions of the query.\n        raw_create, raw_columns = self.column_re.search(create_table).groups()\n\n        # Clean up the individual column definitions.\n        split_columns = self.column_split_re.findall(raw_columns)\n        column_defs = [col.strip() for col in split_columns]\n\n        new_column_defs = []\n        new_column_names = []\n        original_column_names = []\n        constraint_terms = ('foreign ', 'primary ', 'constraint ', 'check ')\n\n        for column_def in column_defs:\n            column_name, = self.column_name_re.match(column_def).groups()\n\n            if column_name == column_to_update:\n                new_column_def = fn(column_name, column_def)\n                if new_column_def:\n                    new_column_defs.append(new_column_def)\n                    original_column_names.append(column_name)\n                    column_name, = self.column_name_re.match(\n                        new_column_def).groups()\n                    new_column_names.append(column_name)\n            else:\n                new_column_defs.append(column_def)\n\n                # Avoid treating constraints as columns.\n                if not column_def.lower().startswith(constraint_terms):\n                    new_column_names.append(column_name)\n                    original_column_names.append(column_name)\n\n        # Create a mapping of original columns to new columns.\n        original_to_new = dict(zip(original_column_names, new_column_names))\n        new_column = original_to_new.get(column_to_update)\n\n        fk_filter_fn = lambda column_def: column_def\n        if not new_column:\n            # Remove any foreign keys associated with this column.\n            fk_filter_fn = lambda column_def: None\n        elif new_column != column_to_update:\n            # Update any foreign keys for this column.\n            fk_filter_fn = lambda column_def: self.fk_re.sub(\n                'FOREIGN KEY (\"%s\") ' % new_column,\n                column_def)\n\n        cleaned_columns = []\n        for column_def in new_column_defs:\n            match = self.fk_re.match(column_def)\n            if match is not None and match.groups()[0] == column_to_update:\n                column_def = fk_filter_fn(column_def)\n            if column_def:\n                cleaned_columns.append(column_def)\n\n        # Update the name of the new CREATE TABLE query.\n        temp_table = table + '__tmp__'\n        rgx = re.compile(r'(\"?)%s(\"?)' % re.escape(table), re.I)\n        create = rgx.sub(\n            r'\\1%s\\2' % temp_table,\n            raw_create)\n\n        # Create the new table.\n        columns = ', '.join(cleaned_columns)\n        queries = [\n            NodeList([SQL('DROP TABLE IF EXISTS'), Entity(temp_table)]),\n            SQL('%s (%s)' % (create.strip(), columns))]\n\n        # Populate new table.\n        populate_table = NodeList((\n            SQL('INSERT INTO'),\n            Entity(temp_table),\n            EnclosedNodeList([Entity(col) for col in new_column_names]),\n            SQL('SELECT'),\n            CommaNodeList([Entity(col) for col in original_column_names]),\n            SQL('FROM'),\n            Entity(table)))\n        drop_original = NodeList([SQL('DROP TABLE'), Entity(table)])\n\n        # Drop existing table and rename temp table.\n        queries += [\n            populate_table,\n            drop_original,\n            self.rename_table(temp_table, table)]\n\n        # Re-create user-defined indexes. User-defined indexes will have a\n        # non-empty SQL attribute.\n        for index in filter(lambda idx: idx.sql, indexes):\n            if column_to_update not in index.columns:\n                queries.append(SQL(index.sql))\n            elif new_column:\n                sql = self._fix_index(index.sql, column_to_update, new_column)\n                if sql is not None:\n                    queries.append(SQL(sql))\n\n        return queries\n\n    def _fix_index(self, sql, column_to_update, new_column):\n        # Split on the name of the column to update. If it splits into two\n        # pieces, then there's no ambiguity and we can simply replace the\n        # old with the new.\n        parts = sql.split(column_to_update)\n        if len(parts) == 2:\n            return sql.replace(column_to_update, new_column)\n\n        # Find the list of columns in the index expression.\n        lhs, rhs = sql.rsplit('(', 1)\n\n        # Apply the same \"split in two\" logic to the column list portion of\n        # the query.\n        if len(rhs.split(column_to_update)) == 2:\n            return '%s(%s' % (lhs, rhs.replace(column_to_update, new_column))\n\n        # Strip off the trailing parentheses and go through each column.\n        parts = rhs.rsplit(')', 1)[0].split(',')\n        columns = [part.strip('\"`[]\\' ') for part in parts]\n\n        # `columns` looks something like: ['status', 'timestamp\" DESC']\n        # https://www.sqlite.org/lang_keywords.html\n        # Strip out any junk after the column name.\n        clean = []\n        for column in columns:\n            if re.match(r'%s(?:[\\'\"`\\]]?\\s|$)' % re.escape(column_to_update),\n                        column):\n                column = new_column + column[len(column_to_update):]\n            clean.append(column)\n\n        return '%s(%s)' % (lhs, ', '.join('\"%s\"' % c for c in clean))\n\n    @operation\n    def drop_column(self, table, column_name, cascade=True, legacy=False):\n        if sqlite3.sqlite_version_info >= (3, 35, 0) and not legacy:\n            ctx = self.make_context()\n            (self._alter_table(ctx, table)\n             .literal(' DROP COLUMN ')\n             .sql(Entity(column_name)))\n            return ctx\n        return self._update_column(table, column_name, lambda a, b: None)\n\n    @operation\n    def rename_column(self, table, old_name, new_name, legacy=False):\n        if sqlite3.sqlite_version_info >= (3, 25, 0) and not legacy:\n            return (self\n                    ._alter_table(self.make_context(), table)\n                    .literal(' RENAME COLUMN ')\n                    .sql(Entity(old_name))\n                    .literal(' TO ')\n                    .sql(Entity(new_name)))\n        def _rename(column_name, column_def):\n            # Only replace the leading column name identifier to avoid\n            # corrupting type names, defaults, or constraints that happen\n            # to contain the column name as a substring.\n            return re.sub(\n                r'([\"\\'`]?)%s\\1' % re.escape(column_name),\n                r'\\g<1>%s\\1' % new_name,\n                column_def,\n                count=1)\n            return column_def.replace(column_name, new_name)\n        return self._update_column(table, old_name, _rename)\n\n    @operation\n    def add_not_null(self, table, column):\n        def _add_not_null(column_name, column_def):\n            return column_def + ' NOT NULL'\n        return self._update_column(table, column, _add_not_null)\n\n    @operation\n    def drop_not_null(self, table, column):\n        def _drop_not_null(column_name, column_def):\n            return column_def.replace('NOT NULL', '')\n        return self._update_column(table, column, _drop_not_null)\n\n    @operation\n    def add_column_default(self, table, column, default):\n        if default is None:\n            raise ValueError('`default` must be not None/NULL.')\n        if callable_(default):\n            default = default()\n        if (isinstance(default, str) and not default.endswith((')', \"'\"))\n            and not default.isdigit()):\n            default = \"'%s'\" % default.replace(\"'\", \"''\")\n        def _add_default(column_name, column_def):\n            # Try to handle SQL functions and string literals, otherwise quote.\n            return column_def + ' DEFAULT %s' % default\n        return self._update_column(table, column, _add_default)\n\n    @operation\n    def drop_column_default(self, table, column):\n        def _drop_default(column_name, column_def):\n            col = re.sub(r'DEFAULT\\s+[\\w\"\\'\\(\\)]+(\\s|$)', '', column_def,\n                         flags=re.I)\n            return col.strip()\n        return self._update_column(table, column, _drop_default)\n\n    @operation\n    def alter_column_type(self, table, column, field, cast=None):\n        if cast is not None:\n            raise ValueError('alter_column_type() does not support cast with '\n                             'Sqlite.')\n        ctx = self.make_context()\n        def _alter_column_type(column_name, column_def):\n            node_list = field.ddl(ctx)\n            sql, _ = ctx.sql(Entity(column)).sql(node_list).query()\n            return sql\n        return self._update_column(table, column, _alter_column_type)\n\n    @operation\n    def add_constraint(self, table, name, constraint):\n        raise NotImplementedError\n\n    @operation\n    def drop_constraint(self, table, name):\n        raise NotImplementedError\n\n    @operation\n    def add_foreign_key_constraint(self, table, column_name, field,\n                                   on_delete=None, on_update=None):\n        raise NotImplementedError\n\n\ndef migrate(*operations, **kwargs):\n    for operation in operations:\n        operation.run()\n"
  },
  {
    "path": "playhouse/mysql_ext.py",
    "content": "import json\n\ntry:\n    import mysql.connector as mysql_connector\nexcept ImportError:\n    mysql_connector = None\ntry:\n    import mariadb\nexcept ImportError:\n    mariadb = None\n\nfrom peewee import ImproperlyConfigured\nfrom peewee import Insert\nfrom peewee import MySQLDatabase\nfrom peewee import Node\nfrom peewee import NodeList\nfrom peewee import SQL\nfrom peewee import TextField\nfrom peewee import fn\nfrom playhouse.pool import _PooledMySQLDatabase\n\n\nclass MySQLConnectorDatabase(MySQLDatabase):\n    def _connect(self):\n        if mysql_connector is None:\n            raise ImproperlyConfigured('MySQL connector not installed!')\n        return mysql_connector.connect(db=self.database, autocommit=True,\n                                       **self.connect_params)\n\n    def cursor(self, named_cursor=None):\n        if self.is_closed():\n            if self.autoconnect:\n                self.connect()\n            else:\n                raise InterfaceError('Error, database connection not opened.')\n        return self._state.conn.cursor(buffered=True)\n\n    def get_binary_type(self):\n        return mysql_connector.Binary\n\n\nclass PooledMySQLConnectorDatabase(_PooledMySQLDatabase,\n                                   MySQLConnectorDatabase):\n    pass\n\n\nclass MariaDBConnectorDatabase(MySQLDatabase):\n    def _connect(self):\n        if mariadb is None:\n            raise ImproperlyConfigured('mariadb connector not installed!')\n        self.connect_params.pop('charset', None)\n        self.connect_params.pop('sql_mode', None)\n        self.connect_params.pop('use_unicode', None)\n        return mariadb.connect(db=self.database, autocommit=True,\n                               **self.connect_params)\n\n    def cursor(self, named_cursor=None):\n        if self.is_closed():\n            if self.autoconnect:\n                self.connect()\n            else:\n                raise InterfaceError('Error, database connection not opened.')\n        return self._state.conn.cursor(buffered=True)\n\n    def _set_server_version(self, conn):\n        version = conn.server_version\n        version, point = divmod(version, 100)\n        version, minor = divmod(version, 100)\n        self.server_version = (version, minor, point)\n        if self.server_version >= (10, 5, 0):\n            self.returning_clause = True\n\n    def last_insert_id(self, cursor, query_type=None):\n        if not self.returning_clause:\n            return cursor.lastrowid\n        elif query_type == Insert.SIMPLE:\n            try:\n                return cursor[0][0]\n            except (AttributeError, IndexError):\n                return cursor.lastrowid\n        return cursor\n\n    def get_binary_type(self):\n        return mariadb.Binary\n\n\nclass PooledMariaDBConnectorDatabase(_PooledMySQLDatabase,\n                                     MariaDBConnectorDatabase):\n    pass\n\n\nclass JSONField(TextField):\n    field_type = 'JSON'\n\n    def __init__(self, json_dumps=None, json_loads=None, **kwargs):\n        self._json_dumps = json_dumps or json.dumps\n        self._json_loads = json_loads or json.loads\n        super(JSONField, self).__init__(**kwargs)\n\n    def python_value(self, value):\n        if value is not None:\n            try:\n                return self._json_loads(value)\n            except (TypeError, ValueError):\n                return value\n\n    def db_value(self, value):\n        if value is not None:\n            if not isinstance(value, Node):\n                value = self._json_dumps(value)\n            return value\n\n    def extract(self, path):\n        return fn.json_extract(self, path)\n\n\ndef Match(columns, expr, modifier=None):\n    if isinstance(columns, (list, tuple)):\n        match = fn.MATCH(*columns)  # Tuple of one or more columns / fields.\n    else:\n        match = fn.MATCH(columns)  # Single column / field.\n    args = expr if modifier is None else NodeList((expr, SQL(modifier)))\n    return NodeList((match, fn.AGAINST(args)))\n"
  },
  {
    "path": "playhouse/pool.py",
    "content": "import functools\nimport heapq\nimport logging\nimport threading\nimport time\nfrom collections import namedtuple\n\nfrom peewee import MySQLDatabase\nfrom peewee import PostgresqlDatabase\nfrom peewee import SqliteDatabase\n\nlogger = logging.getLogger('peewee.pool')\n\n\ndef make_int(val):\n    if val is not None and not isinstance(val, (int, float)):\n        return int(val)\n    return val\n\n\nclass MaxConnectionsExceeded(ValueError): pass\n\n\nPoolConnection = namedtuple('PoolConnection', ('timestamp', 'connection',\n                                               'checked_out'))\n\nclass _sentinel(object):\n    def __lt__(self, other):\n        return True\n\n\ndef locked(fn):\n    @functools.wraps(fn)\n    def inner(self, *args, **kwargs):\n        with self._pool_lock:\n            return fn(self, *args, **kwargs)\n    return inner\n\n\nclass PooledDatabase(object):\n    def __init__(self, database, max_connections=20, stale_timeout=None,\n                 timeout=None, **kwargs):\n        self._max_connections = make_int(max_connections)\n        self._stale_timeout = make_int(stale_timeout)\n        self._wait_timeout = make_int(timeout)\n        if self._wait_timeout == 0:\n            self._wait_timeout = float('inf')\n\n        # Lock for pool operations and condition for notifying when connection\n        # is released back to pool.\n        self._pool_lock = threading.RLock()\n        self._pool_available = threading.Condition(self._pool_lock)\n\n        # Available / idle connections stored in a heap, sorted oldest first.\n        self._connections = []\n\n        # Counter used for tie-breaker in heap (so we don't try comparing\n        # connection against connection).\n        self._heap_counter = 0\n\n        # Mapping of connection id to PoolConnection. Ordinarily we would want\n        # to use something like a WeakKeyDictionary, but Python typically won't\n        # allow us to create weak references to connection objects.\n        self._in_use = {}\n\n        # Use the memory address of the connection as the key in the event the\n        # connection object is not hashable. Connections will not get\n        # garbage-collected, however, because a reference to them will persist\n        # in \"_in_use\" as long as the conn has not been closed.\n        self.conn_key = id\n\n        super(PooledDatabase, self).__init__(database, **kwargs)\n\n    def init(self, database, max_connections=None, stale_timeout=None,\n             timeout=None, **connect_kwargs):\n        super(PooledDatabase, self).init(database, **connect_kwargs)\n        if max_connections is not None:\n            self._max_connections = make_int(max_connections)\n        if stale_timeout is not None:\n            self._stale_timeout = make_int(stale_timeout)\n        if timeout is not None:\n            self._wait_timeout = make_int(timeout)\n            if self._wait_timeout == 0:\n                self._wait_timeout = float('inf')\n\n    def connect(self, reuse_if_open=False):\n        if not self._wait_timeout:\n            return super(PooledDatabase, self).connect(reuse_if_open)\n\n        deadline = time.monotonic() + self._wait_timeout\n        while True:\n            try:\n                return super(PooledDatabase, self).connect(reuse_if_open)\n            except MaxConnectionsExceeded:\n                remaining = deadline - time.monotonic()\n                if remaining <= 0:\n                    raise MaxConnectionsExceeded(\n                        'Max connections exceeded, timed out attempting to '\n                        'connect.')\n                with self._pool_available:\n                    self._pool_available.wait(timeout=min(remaining, 1.0))\n\n    @locked\n    def _connect(self):\n        while self._connections:\n            try:\n                # Remove the oldest connection from the heap.\n                ts, _counter, conn = heapq.heappop(self._connections)\n            except IndexError:\n                break\n\n            key = self.conn_key(conn)\n            if self._is_closed(conn):\n                # Connection closed either by user or by driver - discard.\n                logger.debug('Connection %s was closed, discarding.', key)\n                continue\n\n            if self._stale_timeout and self._is_stale(ts):\n                logger.debug('Connection %s was stale, closing.', key)\n                self._close_raw(conn)\n                continue\n\n            # Connection OK to use.\n            self._in_use[key] = PoolConnection(ts, conn, time.time())\n            return conn\n\n        if self._max_connections and (\n                len(self._in_use) >= self._max_connections):\n            raise MaxConnectionsExceeded('Exceeded maximum connections.')\n\n        conn = super(PooledDatabase, self)._connect()\n        ts = time.time()\n        key = self.conn_key(conn)\n        logger.debug('Created new connection %s.', key)\n        self._in_use[key] = PoolConnection(ts, conn, time.time())\n        return conn\n\n    def _is_stale(self, timestamp):\n        # Called on check-out and check-in to ensure the connection has\n        # not outlived the stale timeout.\n        return (time.time() - timestamp) > self._stale_timeout\n\n    def _is_closed(self, conn):\n        return False\n\n    def _can_reuse(self, conn):\n        # Called on check-in to make sure the connection can be re-used.\n        return True\n\n    def _close_raw(self, conn):\n        try:\n            super(PooledDatabase, self)._close(conn)\n        except Exception:\n            logger.debug('Error closing connection %s.', self.conn_key(conn),\n                         exc_info=True)\n\n    @locked\n    def _close(self, conn, close_conn=False):\n        # if close_conn == True, close underlying driver connection and remove\n        # from _in_use tracking. Do not return to available conns.\n        key = self.conn_key(conn)\n\n        if close_conn:\n            self._in_use.pop(key, None)\n            self._close_raw(conn)\n            return\n\n        if key not in self._in_use:\n            logger.debug('Connection %s not in use, ignoring close.', key)\n            return\n\n        pool_conn = self._in_use.pop(key)\n        if self._stale_timeout and self._is_stale(pool_conn.timestamp):\n            logger.debug('Closing stale connection %s on check-in.', key)\n            self._close_raw(conn)\n        elif not self._can_reuse(conn):\n            logger.debug('Connection %s not reusable, closing.', key)\n            self._close_raw(conn)\n        else:\n            logger.debug('Returning %s to pool.', key)\n            self._heap_counter += 1\n            heapq.heappush(self._connections,\n                           (pool_conn.timestamp, self._heap_counter, conn))\n\n        # Wake up thread that may be waiting on connection.\n        self._pool_available.notify()\n\n    @locked\n    def manual_close(self):\n        \"\"\"\n        Close the underlying connection without returning it to the pool.\n        \"\"\"\n        if self.is_closed():\n            return False\n\n        # Obtain reference to the connection in-use by the calling thread.\n        conn = self.connection()\n        key = self.conn_key(conn)\n\n        # Remove from _in_use so that subsequent self.close() won't try to\n        # restore it to the pool.\n        self._in_use.pop(key, None)\n        self.close()\n        self._close_raw(conn)\n\n    @locked\n    def close_idle(self):\n        # Close any open connections that are not currently in-use.\n        idle = self._connections\n        self._connections = []\n        for _, _, conn in idle:\n            self._close_raw(conn)\n\n    @locked\n    def close_stale(self, age=600):\n        # Close any connections that are in-use but were checked out quite some\n        # time ago and can be considered stale. May close connections in use by\n        # running threads.\n        cutoff = time.time() - age\n        n = 0\n        for key, pool_conn in list(self._in_use.items()):\n            if pool_conn.checked_out < cutoff:\n                self._close_raw(pool_conn.connection)\n                del self._in_use[key]\n                n += 1\n\n        self._pool_available.notify_all()\n        return n\n\n    @locked\n    def close_all(self):\n        # Close all connections -- available and in-use. Warning: may break any\n        # active connections used by other threads.\n        self.close()\n        self.close_idle()\n        in_use, self._in_use = self._in_use, {}\n        for pool_conn in in_use.values():\n            self._close_raw(pool_conn.connection)\n\n        self._pool_available.notify_all()\n\n\nclass _PooledMySQLDatabase(PooledDatabase):\n    def _is_closed(self, conn):\n        if self.server_version[0] == 8:\n            args = ()\n        else:\n            args = (False,)\n        try:\n            conn.ping(*args)\n        except:\n            return True\n        return False\n\nclass PooledMySQLDatabase(_PooledMySQLDatabase, MySQLDatabase):\n    pass\n\n\nclass _PooledPostgresqlDatabase(PooledDatabase):\n    def _is_closed(self, conn):\n        if conn.closed:\n            return True\n        return self._adapter.is_connection_closed(conn)\n\n    def _can_reuse(self, conn):\n        return self._adapter.is_connection_reusable(conn)\n\nclass PooledPostgresqlDatabase(_PooledPostgresqlDatabase, PostgresqlDatabase):\n    pass\n\n\nclass _PooledSqliteDatabase(PooledDatabase):\n    def _is_closed(self, conn):\n        try:\n            conn.total_changes\n        except:\n            return True\n        return False\n\nclass PooledSqliteDatabase(_PooledSqliteDatabase, SqliteDatabase):\n    pass\n"
  },
  {
    "path": "playhouse/postgres_ext.py",
    "content": "import json\nimport logging\nimport uuid\n\nfrom peewee import *\nfrom peewee import ColumnBase\nfrom peewee import Expression\nfrom peewee import FieldDatabaseHook\nfrom peewee import Node\nfrom peewee import NodeList\nfrom peewee import Psycopg2Adapter\nfrom peewee import Psycopg3Adapter\nfrom peewee import __exception_wrapper__\nfrom playhouse.pool import _PooledPostgresqlDatabase\n\ntry:\n    from psycopg2cffi import compat\n    compat.register()\nexcept ImportError:\n    pass\n\ntry:\n    from psycopg2.extras import register_hstore\nexcept ImportError:\n    def register_hstore(*args): pass\n\ntry:\n    from psycopg.types import TypeInfo\n    from psycopg.types.hstore import register_hstore as register_hstore_pg3\nexcept ImportError:\n    def register_hstore_pg3(*args): pass\n\n\nlogger = logging.getLogger('peewee')\n\n\nHCONTAINS_DICT = '@>'\nHCONTAINS_KEYS = '?&'\nHCONTAINS_KEY = '?'\nHCONTAINS_ANY_KEY = '?|'\nHKEY = '->'\nHUPDATE = '||'\nACONTAINS = '@>'\nACONTAINED_BY = '<@'\nACONTAINS_ANY = '&&'\nTS_MATCH = '@@'\nJSONB_CONTAINS = '@>'\nJSONB_CONTAINED_BY = '<@'\nJSONB_CONTAINS_KEY = '?'\nJSONB_CONTAINS_ANY_KEY = '?|'\nJSONB_CONTAINS_ALL_KEYS = '?&'\nJSONB_EXISTS = '?'\nJSONB_REMOVE = '-'\nJSONB_PATH_REMOVE = '#-'\nJSONB_PATH = '#>'\n\n\nclass Json(Node):\n    # Fallback JSON handler.\n    __slots__ = ('value',)\n\n    def __init__(self, value, dumps=None):\n        self.value = value\n        self.dumps = dumps or json.dumps\n\n    def __sql__(self, ctx):\n        return ctx.value(self.value, self.dumps)\n\n\nclass _LookupNode(ColumnBase):\n    def __init__(self, node, parts):\n        self.node = node\n        self.parts = parts\n        super(_LookupNode, self).__init__()\n\n    def clone(self):\n        return type(self)(self.node, list(self.parts))\n\n    def __hash__(self):\n        return hash((self.__class__.__name__, id(self)))\n\n\nclass ObjectSlice(_LookupNode):\n    @classmethod\n    def create(cls, node, value):\n        if isinstance(value, slice):\n            stop = value.stop - 1 if value.stop is not None else None\n            parts = [value.start or 0, stop]\n        elif isinstance(value, int):\n            parts = [value]\n        elif isinstance(value, Node):\n            parts = value\n        else:\n            # Assumes colon-separated integer indexes.\n            parts = [int(i) for i in value.split(':')]\n        return cls(node, parts)\n\n    def __sql__(self, ctx):\n        ctx.sql(self.node)\n        if isinstance(self.parts, Node):\n            ctx.literal('[').sql(self.parts).literal(']')\n        else:\n            ctx.literal('[%s]' % ':'.join([str(p + 1) if p is not None else ''\n                                           for p in self.parts]))\n        return ctx\n\n    def __getitem__(self, value):\n        return ObjectSlice.create(self, value)\n\n\nclass IndexedFieldMixin(object):\n    default_index_type = 'GIN'\n\n    def __init__(self, *args, **kwargs):\n        kwargs.setdefault('index', True)  # By default, use an index.\n        super(IndexedFieldMixin, self).__init__(*args, **kwargs)\n\n\nclass ArrayField(IndexedFieldMixin, Field):\n    passthrough = True\n\n    def __init__(self, field_class=IntegerField, field_kwargs=None,\n                 dimensions=1, convert_values=False, *args, **kwargs):\n        self.__field = field_class(**(field_kwargs or {}))\n        self.dimensions = dimensions\n        self.convert_values = convert_values\n        self.field_type = self.__field.field_type\n        super(ArrayField, self).__init__(*args, **kwargs)\n\n    def bind(self, model, name, set_attribute=True):\n        ret = super(ArrayField, self).bind(model, name, set_attribute)\n        self.__field.bind(model, '__array_%s' % name, False)\n        return ret\n\n    def ddl_datatype(self, ctx):\n        data_type = self.__field.ddl_datatype(ctx)\n        return NodeList((data_type, SQL('[]' * self.dimensions)), glue='')\n\n    def db_value(self, value):\n        if value is None or isinstance(value, Node):\n            return value\n        elif self.convert_values:\n            return self._process(self.__field.db_value, value, self.dimensions)\n        else:\n            return value if isinstance(value, list) else list(value)\n\n    def python_value(self, value):\n        if self.convert_values and value is not None:\n            conv = self.__field.python_value\n            if isinstance(value, list):\n                return self._process(conv, value, self.dimensions)\n            else:\n                return conv(value)\n        else:\n            return value\n\n    def _process(self, conv, value, dimensions):\n        dimensions -= 1\n        if dimensions == 0:\n            return [conv(v) for v in value]\n        else:\n            return [self._process(conv, v, dimensions) for v in value]\n\n    def __getitem__(self, value):\n        return ObjectSlice.create(self, value)\n\n    def _e(op):\n        def inner(self, rhs):\n            return Expression(self, op, ArrayValue(self, rhs))\n        return inner\n    __eq__ = _e(OP.EQ)\n    __ne__ = _e(OP.NE)\n    __gt__ = _e(OP.GT)\n    __ge__ = _e(OP.GTE)\n    __lt__ = _e(OP.LT)\n    __le__ = _e(OP.LTE)\n    __hash__ = Field.__hash__\n\n    def contains(self, *items):\n        return Expression(self, ACONTAINS, ArrayValue(self, items))\n\n    def contains_any(self, *items):\n        return Expression(self, ACONTAINS_ANY, ArrayValue(self, items))\n\n    def contained_by(self, *items):\n        return Expression(self, ACONTAINED_BY, ArrayValue(self, items))\n\n\nclass ArrayValue(Node):\n    def __init__(self, field, value):\n        self.field = field\n        self.value = value\n\n    def __sql__(self, ctx):\n        return (ctx\n                .sql(AsIs(self.value))\n                .literal('::')\n                .sql(self.field.ddl_datatype(ctx)))\n\n\nclass DateTimeTZField(DateTimeField):\n    field_type = 'TIMESTAMPTZ'\n\n\nclass HStoreField(IndexedFieldMixin, Field):\n    field_type = 'HSTORE'\n    __hash__ = Field.__hash__\n\n    def __getitem__(self, key):\n        return Expression(self, HKEY, Value(key))\n\n    def keys(self):\n        return fn.akeys(self)\n\n    def values(self):\n        return fn.avals(self)\n\n    def items(self):\n        return fn.hstore_to_matrix(self)\n\n    def slice(self, *args):\n        return fn.slice(self, AsIs(list(args)))\n\n    def exists(self, key):\n        return fn.exist(self, key)\n\n    def defined(self, key):\n        return fn.defined(self, key)\n\n    def update(self, __data=None, **data):\n        if __data is not None:\n            data.update(__data)\n        return Expression(self, HUPDATE, data)\n\n    def delete(self, *keys):\n        value = Cast(AsIs(list(keys)), 'text[]')\n        return fn.delete(self, value)\n\n    def contains(self, value):\n        if isinstance(value, dict):\n            rhs = AsIs(value)\n            return Expression(self, HCONTAINS_DICT, rhs)\n        elif isinstance(value, (list, tuple)):\n            rhs = AsIs(value)\n            return Expression(self, HCONTAINS_KEYS, rhs)\n        return Expression(self, HCONTAINS_KEY, value)\n\n    def contains_any(self, *keys):\n        return Expression(self, HCONTAINS_ANY_KEY, AsIs(list(keys)))\n\n\nclass _JsonLookupBase(_LookupNode):\n    def __init__(self, node, parts, as_json=False):\n        super(_JsonLookupBase, self).__init__(node, parts)\n        self._jsonb = getattr(node, '_json_type', 'jsonb') == 'jsonb'\n        self._as_json = as_json\n\n    def clone(self):\n        return type(self)(self.node, list(self.parts), self._as_json)\n\n    @Node.copy\n    def as_json(self, as_json=True):\n        self._as_json = as_json\n\n    def concat(self, rhs):\n        if not isinstance(rhs, Node):\n            rhs = self.node.json_type(rhs)\n        return Expression(self.as_json(True), OP.CONCAT, rhs)\n\n    def contains(self, other):\n        if not isinstance(other, Node):\n            other = self.node.json_type(other)\n        return Expression(self.as_json(True), JSONB_CONTAINS, other)\n\n    def contained_by(self, other):\n        if not isinstance(other, Node):\n            other = self.node.json_type(other)\n        return Expression(self.as_json(True), JSONB_CONTAINED_BY, other)\n\n    def contains_any(self, *keys):\n        return Expression(\n            self.as_json(True),\n            JSONB_CONTAINS_ANY_KEY,\n            AsIs(list(keys), False))\n\n    def contains_all(self, *keys):\n        return Expression(\n            self.as_json(True),\n            JSONB_CONTAINS_ALL_KEYS,\n            AsIs(list(keys), False))\n\n    def has_key(self, key):\n        return Expression(self.as_json(True), JSONB_CONTAINS_KEY, key)\n\n    def remove(self):\n        parts = [str(p) if isinstance(p, int) else p for p in self.parts]\n        value = AsIs(parts, False)\n        return Expression(self.node, JSONB_PATH_REMOVE, value)\n\n    def length(self):\n        func = fn.jsonb_array_length if self._jsonb else fn.json_array_length\n        return func(self.as_json(True))\n\n    def extract(self, *path):\n        path = [str(p) if isinstance(p, int) else p for p in path]\n        func = fn.jsonb_extract_path if self._jsonb else fn.json_extract_path\n        return func(self.as_json(True), *path)\n\n    def path(self, *keys):\n        return JsonPath(self.as_json(True), keys, as_json=True)\n\n\nclass JsonLookup(_JsonLookupBase):\n    def __getitem__(self, value):\n        return JsonLookup(self.node, self.parts + [value], self._as_json)\n\n    def __sql__(self, ctx):\n        ctx.sql(self.node)\n        for part in self.parts[:-1]:\n            ctx.literal('->').sql(part)\n        if self.parts:\n            (ctx\n             .literal('->' if self._as_json else '->>')\n             .sql(self.parts[-1]))\n\n        return ctx\n\n\nclass JsonPath(_JsonLookupBase):\n    def __sql__(self, ctx):\n        return (ctx\n                .sql(self.node)\n                .literal('#>' if self._as_json else '#>>')\n                .sql(Value('{%s}' % ','.join(map(str, self.parts)))))\n\n\nclass JSONField(FieldDatabaseHook, Field):\n    field_type = 'JSON'\n    _json_datatype = 'json'\n\n    def __init__(self, dumps=None, **kwargs):\n        self._dumps = dumps\n        super(JSONField, self).__init__(**kwargs)\n\n    def _db_hook(self, database):\n        if database is None or not hasattr(database, '_adapter'):\n            self.json_type = Json\n            self.cast_json_case = True\n        else:\n            self.json_type = database._adapter.json_type\n            self.cast_json_case = database._adapter.cast_json_case\n\n        if self._dumps:\n            dumps = self._dumps\n            class _Json(self.json_type):\n                def __init__(self, value):\n                    super(_Json, self).__init__(value, dumps=dumps)\n            self.json_type = _Json\n\n    def db_value(self, value):\n        if value is None or isinstance(value, (Node, self.json_type)):\n            return value\n        return self.json_type(value)\n\n    def to_value(self, value, case=False):\n        # CASE WHEN id = 123 THEN x.json_data fails because the expression is\n        # untyped, so we need an explicit cast with psycopg2.\n        if case and self.cast_json_case:\n            return Cast(self.json_type(value), self._json_datatype)\n        return self.json_type(value)\n\n    def __getitem__(self, value):\n        return JsonLookup(self, [value])\n\n    def path(self, *keys):\n        return JsonPath(self, keys, as_json=True)\n\n    def concat(self, value):\n        if not isinstance(value, Node):\n            value = self.json_type(value)\n        return super(JSONField, self).concat(value)\n\n    def length(self):\n        return fn.json_array_length(self)\n\n    def extract(self, *path):\n        path = [str(p) if isinstance(p, int) else p for p in path]\n        return fn.json_extract_path(self, *path)\n\n\nclass BinaryJSONField(IndexedFieldMixin, JSONField):\n    field_type = 'JSONB'\n    _json_datatype = 'jsonb'\n    __hash__ = Field.__hash__\n\n    def _db_hook(self, database):\n        if database is None or not hasattr(database, '_adapter'):\n            self.json_type = Json\n            self.cast_json_case = True\n        else:\n            self.json_type = database._adapter.jsonb_type\n            self.cast_json_case = database._adapter.cast_json_case\n\n        if self._dumps:\n            dumps = self._dumps\n            class _Json(self.json_type):\n                def __init__(self, value):\n                    super(_Json, self).__init__(value, dumps=dumps)\n            self.json_type = _Json\n\n    def contains(self, other):\n        if not isinstance(other, Node):\n            other = self.json_type(other)\n        return Expression(self, JSONB_CONTAINS, other)\n\n    def contained_by(self, other):\n        if not isinstance(other, Node):\n            other = self.json_type(other)\n        return Expression(self, JSONB_CONTAINED_BY, other)\n\n    def contains_any(self, *items):\n        return Expression(\n            self,\n            JSONB_CONTAINS_ANY_KEY,\n            AsIs(list(items), False))\n\n    def contains_all(self, *items):\n        return Expression(\n            self,\n            JSONB_CONTAINS_ALL_KEYS,\n            AsIs(list(items), False))\n\n    def has_key(self, key):\n        return Expression(self, JSONB_CONTAINS_KEY, Value(key, False))\n\n    def remove(self, *items):\n        value = Cast(AsIs(list(items), False), 'text[]')\n        return Expression(self, JSONB_REMOVE, value)\n\n    def length(self):\n        return fn.jsonb_array_length(self)\n\n    def extract(self, *path):\n        path = [str(p) if isinstance(p, int) else p for p in path]\n        return fn.jsonb_extract_path(self, *path)\n\n\nclass TSVectorField(IndexedFieldMixin, TextField):\n    field_type = 'TSVECTOR'\n    __hash__ = Field.__hash__\n\n    def match(self, query, language=None, plain=False):\n        params = (language, query) if language is not None else (query,)\n        func = fn.plainto_tsquery if plain else fn.to_tsquery\n        return Expression(self, TS_MATCH, func(*params))\n\n\ndef Match(field, query, language=None):\n    params = (language, query) if language is not None else (query,)\n    field_params = (language, field) if language is not None else (field,)\n    return Expression(\n        fn.to_tsvector(*field_params),\n        TS_MATCH,\n        fn.to_tsquery(*params))\n\n\nclass IntervalField(Field):\n    field_type = 'INTERVAL'\n\n\nclass FetchManyCursor(object):\n    __slots__ = ('cursor', 'array_size', 'exhausted', 'iterable')\n\n    def __init__(self, cursor, array_size=None):\n        self.cursor = cursor\n        self.array_size = array_size or cursor.itersize\n        self.exhausted = False\n        self.iterable = self.row_gen()\n\n    @property\n    def description(self):\n        return self.cursor.description\n\n    def close(self):\n        if self.cursor is not None and not self.cursor.closed:\n            self.cursor.close()\n\n    def row_gen(self):\n        try:\n            while True:\n                rows = self.cursor.fetchmany(self.array_size)\n                if not rows:\n                    return\n                for row in rows:\n                    yield row\n        finally:\n            self.close()\n\n    def fetchone(self):\n        if self.exhausted:\n            return\n        try:\n            return next(self.iterable)\n        except StopIteration:\n            self.exhausted = True\n\n\nclass ServerSideQuery(Node):\n    def __init__(self, query, array_size=None):\n        self.query = query\n        self.array_size = array_size\n        self._cursor_wrapper = None\n\n    def __sql__(self, ctx):\n        return self.query.__sql__(ctx)\n\n    def __iter__(self):\n        if self._cursor_wrapper is None:\n            self._execute(self.query._database)\n        return iter(self._cursor_wrapper.iterator())\n\n    def close(self):\n        if self._cursor_wrapper is not None:\n            self._cursor_wrapper.cursor.close()\n            self._cursor_wrapper = None\n            return True\n        return False\n\n    def iterator(self):\n        if self._cursor_wrapper is None:\n            self._execute(self.query._database)\n        return self._cursor_wrapper.iterator()\n\n    def _execute(self, database):\n        if self._cursor_wrapper is None:\n            cursor = database.execute(self.query, named_cursor=True,\n                                      array_size=self.array_size)\n            self._cursor_wrapper = self.query._get_cursor_wrapper(cursor)\n        return self._cursor_wrapper\n\n\ndef ServerSide(query, array_size=None):\n    server_side_query = ServerSideQuery(query, array_size=array_size)\n    for row in server_side_query:\n        yield row\n\n\nclass _empty_object(object):\n    __slots__ = ()\n    def __nonzero__(self):\n        return False\n    __bool__ = __nonzero__\n\n\nclass Psycopg2ExtAdapter(Psycopg2Adapter):\n    def register_hstore(self, conn):\n        register_hstore(conn)\n\n    def server_side_cursor(self, conn):\n        # psycopg2 does not allow us to use these in autocommit, even if we ARE\n        # inside a transaction - so specify withhold (not desirable!).\n        return conn.cursor(name=str(uuid.uuid1()), withhold=True)\n\n\nclass Psycopg3ExtAdapter(Psycopg3Adapter):\n    def register_hstore(self, conn):\n        info = TypeInfo.fetch(conn, 'hstore')\n        register_hstore_pg3(info, conn)\n\n    def server_side_cursor(self, conn):\n        return conn.cursor(name=str(uuid.uuid1()))\n\n\nclass PostgresqlExtDatabase(PostgresqlDatabase):\n    psycopg2_adapter = Psycopg2ExtAdapter\n    psycopg3_adapter = Psycopg3ExtAdapter\n\n    def __init__(self, *args, **kwargs):\n        self._register_hstore = kwargs.pop('register_hstore', False)\n        self._server_side_cursors = kwargs.pop('server_side_cursors', False)\n        super(PostgresqlExtDatabase, self).__init__(*args, **kwargs)\n\n    def _connect(self):\n        conn = super(PostgresqlExtDatabase, self)._connect()\n        if self._register_hstore:\n            self._adapter.register_hstore(conn)\n        return conn\n\n    def cursor(self, named_cursor=None):\n        if self.is_closed():\n            if self.autoconnect:\n                self.connect()\n            else:\n                raise InterfaceError('Error, database connection not opened.')\n        if named_cursor:\n            return self._adapter.server_side_cursor(self._state.conn)\n        return self._state.conn.cursor()\n\n    def execute(self, query, named_cursor=False, array_size=None,\n                **context_options):\n        ctx = self.get_sql_context(**context_options)\n        sql, params = ctx.sql(query).query()\n        named_cursor = named_cursor or (self._server_side_cursors and\n                                        sql[:6].lower() == 'select')\n        cursor = self.execute_sql(sql, params, named_cursor=named_cursor)\n        if named_cursor:\n            cursor = FetchManyCursor(cursor, array_size)\n        return cursor\n\n    def execute_sql(self, sql, params=None, named_cursor=None):\n        logger.debug((sql, params))\n        with __exception_wrapper__:\n            cursor = self.cursor(named_cursor=named_cursor)\n            cursor.execute(sql, params or ())\n        return cursor\n\n\nclass PooledPostgresqlExtDatabase(_PooledPostgresqlDatabase, PostgresqlExtDatabase):\n    pass\n\n\nclass Psycopg3Database(PostgresqlExtDatabase):\n    def __init__(self, *args, **kwargs):\n        kwargs['prefer_psycopg3'] = True\n        super(Psycopg3Database, self).__init__(*args, **kwargs)\n\n\nclass PooledPsycopg3Database(_PooledPostgresqlDatabase, Psycopg3Database):\n    pass\n"
  },
  {
    "path": "playhouse/pwasyncio.py",
    "content": "import asyncio\nimport collections\nimport contextvars\nimport json\nimport logging\n\nfrom greenlet import greenlet, getcurrent\nfrom peewee import *\nfrom peewee import _atomic, _savepoint, _transaction\nfrom peewee import __exception_wrapper__\nfrom peewee import Node\nfrom peewee import Psycopg3Adapter\nfrom playhouse.postgres_ext import Json\n\ntry:\n    import aiosqlite\nexcept ImportError:\n    aiosqlite = None\n\ntry:\n    import asyncpg\nexcept ImportError:\n    asyncpg = None\n\ntry:\n    import aiomysql\nexcept ImportError:\n    aiomysql = None\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass MissingGreenletBridge(RuntimeError):\n    pass\n\n\nasync def greenlet_spawn(fn, *args, **kwargs):\n    parent = getcurrent()\n    result = None\n    error = None\n\n    def runner():\n        nonlocal result, error\n        try:\n            result = fn(*args, **kwargs)\n        except BaseException as exc:\n            error = exc\n\n    # Run the sync code in a greenlet - the sync code must use await_()\n    # whenever blocking would occur. await_() transfers a coroutine and control\n    # back up to this runner, which can safely `await` the coroutine before\n    # switching back to the sync code.\n    g = greenlet(runner, parent=parent)\n    g.gr_context = parent.gr_context\n    value = g.switch()\n    while not g.dead:\n        try:\n            value = g.switch(await value)\n        except BaseException as exc:\n            value = g.throw(exc)\n\n    if error:\n        raise error\n    return result\n\n\ndef await_(awaitable):\n    current = getcurrent()\n    parent = current.parent\n    if parent is None:\n        raise MissingGreenletBridge('await_() called outside greenlet_spawn()')\n    return parent.switch(awaitable)\n\n\nclass _State(object):\n    __slots__ = ('conn', 'closed', 'transactions', '_task_id')\n\n    def __init__(self):\n        self._task_id = None\n        self.reset()\n\n    def reset(self):\n        self.conn = None\n        self.closed = True\n        self.transactions = []\n\n\nclass _ConnectionState(object):\n    def __init__(self):\n        self._cv = contextvars.ContextVar('pwasyncio_state')\n        # Central registry: task-id -> _State.  Allows close_pool() to\n        # enumerate *all* live states and release their connections.\n        self._states = {}\n        self._orphaned_conns = []\n\n    def _current(self):\n        task = asyncio.current_task()\n        if task is None:\n            raise RuntimeError('Cannot determine current task')\n        tid = id(task)\n\n        try:\n            state = self._cv.get()\n            if state._task_id == tid:\n                # Re-register if evicted (e.g. by close_pool clearing _states).\n                if tid not in self._states:\n                    self._states[tid] = state\n                    # Unnecessary to register the callback; task is still\n                    # running so the original callback should be present.\n                    # task.add_done_callback(self._on_task_done)\n                return state\n        except LookupError:\n            pass\n\n        if tid in self._states:\n            state = self._states[tid]\n        else:\n            state = _State()\n            state._task_id = tid\n            self._states[tid] = state\n            task.add_done_callback(self._on_task_done)\n\n        # Cache in the contextvar for subsequent calls for task.\n        self._cv.set(state)\n        return state\n\n    def _on_task_done(self, task):\n        tid = id(task)\n        state = self._states.pop(tid, None)\n        if state is not None and state.conn is not None and not state.closed:\n            self._orphaned_conns.append(state.conn)\n            state.reset()\n\n    @property\n    def conn(self):\n        return self._current().conn\n\n    @property\n    def closed(self):\n        return self._current().closed\n\n    @property\n    def transactions(self):\n        return self._current().transactions\n\n    def reset(self):\n        try:\n            state = self._current()\n        except RuntimeError:\n            return\n        state.reset()\n\n    def set_connection(self, conn):\n        state = self._current()\n        state.conn = conn\n        state.closed = False\n\n\nclass _async_transaction_helper(object):\n    async def __aenter__(self):\n        return await self.db.run(self.__enter__)\n\n    async def __aexit__(self, exc_typ, exc, tb):\n        return await self.db.run(self.__exit__, exc_typ, exc, tb)\n\n    async def acommit(self):\n        return await self.db.run(self.commit)\n\n    async def arollback(self):\n        return await self.db.run(self.rollback)\n\n\nclass async_atomic(_async_transaction_helper, _atomic): pass\nclass async_transaction(_async_transaction_helper, _transaction): pass\nclass async_savepoint(_async_transaction_helper, _savepoint): pass\n\n\nclass AsyncDatabaseMixin(object):\n    def __init__(self, database, **kwargs):\n        self._pool_size = kwargs.pop('pool_size', 10)\n        self._pool_min_size = kwargs.pop('pool_min_size', 1)\n        self._acquire_timeout = kwargs.pop('acquire_timeout', 10)\n        super(AsyncDatabaseMixin, self).__init__(database, **kwargs)\n\n        self._state = _ConnectionState()\n        self._pool = None\n        self._pool_lock = asyncio.Lock()\n        self._closing = False  # Guard against use during shutdown.\n\n    def execute_sql(self, sql, params=None):\n        try:\n            return await_(self.aexecute_sql(sql, params or ()))\n        except MissingGreenletBridge as exc:\n            raise MissingGreenletBridge(\n                f'Attempted query {sql} ({params}) outside greenlet runner.') \\\n                    from exc\n\n    async def aexecute_sql(self, sql, params=None):\n        conn = await self.aconnect()\n        with __exception_wrapper__:\n            return await conn.execute(sql, params)\n\n    def connect(self):\n        return await_(self.aconnect())\n\n    async def aconnect(self):\n        if self._closing:\n            raise InterfaceError('Database pool is shutting down.')\n\n        # Drain any connections orphaned by dead tasks.\n        while self._state._orphaned_conns:\n            orphan = self._state._orphaned_conns.pop()\n            await self._pool_release(orphan)\n\n        conn = self._state.conn\n        if conn is None or conn.conn is None:\n            if conn is not None:\n                # Previous connection was invalidated, release it.\n                await self._pool_release(conn)\n            conn = await self._acquire_conn_async()\n            self._state.set_connection(conn)\n        return conn\n\n    def close(self):\n        return await_(self.aclose())\n\n    async def aclose(self):\n        conn = self._state.conn\n        if conn:\n            self._state.reset()\n            logger.debug('Releasing connection %s to pool.', id(conn))\n            await self._pool_release(conn)\n\n    async def _acquire_conn_async(self):\n        async with self._pool_lock:\n            if self._pool is None:\n                self._pool = await self._create_pool_async()\n\n        conn = await self._pool_acquire()\n        logger.debug('Acquired connection %s from pool.', id(conn))\n        return conn\n\n    async def _create_pool_async(self):\n        raise NotImplementedError('Subclasses must implement.')\n\n    async def _pool_acquire(self):\n        raise NotImplementedError('Subclasses must implement.')\n\n    async def _pool_release(self, conn):\n        raise NotImplementedError('Subclasses must implement.')\n\n    async def close_pool(self):\n        self._closing = True\n        try:\n            if self._pool:\n                # Release connections held by any task still in the registry.\n                # We must clear each state BEFORE releasing the connection,\n                # because the await in _pool_release can let the event loop\n                # run pending task-done callbacks.  If the callback sees\n                # state.conn still set it will orphan the same connection,\n                # leading to a double-release that overfills the pool queue.\n                for state in list(self._state._states.values()):\n                    if state.conn and not state.closed:\n                        conn = state.conn\n                        state.reset()\n                        try:\n                            await self._pool_release(conn)\n                        except Exception:\n                            logger.warning(\n                                'Error releasing connection during pool close',\n                                exc_info=True)\n                self._state._states.clear()\n\n                # Drain any connections orphaned by completed tasks.\n                while self._state._orphaned_conns:\n                    orphan = self._state._orphaned_conns.pop()\n                    try:\n                        await self._pool_release(orphan)\n                    except Exception:\n                        logger.warning('Error releasing orphaned connection',\n                                       exc_info=True)\n\n                await self._pool_close()\n                self._pool = None\n        finally:\n            self._closing = False\n\n    async def _pool_close(self):\n        raise NotImplementedError('Subclasses must implement.')\n\n    async def __aenter__(self):\n        await self.run(self.connect)\n        return self\n\n    async def __aexit__(self, exc_typ, exc, tb):\n        await self.run(self.close)\n\n    def atomic(self):\n        return async_atomic(self)\n\n    def transaction(self):\n        return async_transaction(self)\n\n    def savepoint(self):\n        return async_savepoint(self)\n\n    async def acreate_tables(self, *args, **kwargs):\n        return await greenlet_spawn(self.create_tables, *args, **kwargs)\n\n    async def adrop_tables(self, *args, **kwargs):\n        return await greenlet_spawn(self.drop_tables, *args, **kwargs)\n\n    async def aexecute(self, query):\n        query.bind(self)\n        return await self.run(query.execute)\n\n    async def get(self, query):\n        return await self.run(query.get)\n\n    async def list(self, query):\n        return await self.run(list, query)\n\n    async def scalar(self, query):\n        return await self.run(query.scalar)\n\n    async def count(self, query):\n        return await self.run(query.count)\n\n    async def exists(self, query):\n        return await self.run(query.exists)\n\n    async def aprefetch(self, query, *subqueries):\n        return await self.run(prefetch, query, *subqueries)\n\n    async def iterate(self, query, buffer_size=None):\n        # Use similar approach to postgres_ext server-side query impl.\n        query.bind(self)\n        sql, params = query.sql()\n        conn = await self.aconnect()\n        cursor = await conn.execute_iter(sql, params or ())\n        if buffer_size is not None:\n            cursor._buffer_size = buffer_size\n\n        try:\n            wrapper = query._get_cursor_wrapper(cursor)\n            row_iter = wrapper.iterator()\n            _sentinel = object()\n\n            # Cursor wrapper `iterator()` calls fetchone() to grab rows from\n            # the internal buffer. `fetchone()` may dispatch do the event loop\n            # to refill buffer (async).\n            while True:\n                row = await greenlet_spawn(next, row_iter, _sentinel)\n                if row is _sentinel:\n                    break\n                yield row\n        finally:\n            await cursor.aclose()\n\n    async def run(self, fn, *args, **kwargs):\n        return await greenlet_spawn(fn, *args, **kwargs)\n\n    def is_closed(self):\n        try:\n            return self._state.closed\n        except RuntimeError:\n            return True\n\n\nclass CursorAdapter(object):\n    DEFAULT_BUFFER_SIZE = 100\n\n    def __init__(self, rows=None, lastrowid=None, rowcount=None,\n                 description=None, fetch_many=None, cleanup=None,\n                 buffer_size=None):\n        self._rows = rows or []\n        self._idx = 0\n        self.lastrowid = lastrowid\n        self.rowcount = rowcount if rowcount is not None else len(self._rows)\n        self.description = description or []\n\n        # Async server-side cursor support.\n        self._fetch_many = fetch_many\n        self._cleanup = cleanup\n        self._buffer_size = buffer_size or self.DEFAULT_BUFFER_SIZE\n        self._buffer = collections.deque()\n        self._exhausted = False\n\n    def fetchone(self):\n        if self._fetch_many is not None:\n            return self._lazy_fetchone()\n        if self._idx >= len(self._rows):\n            return\n        row = self._rows[self._idx]\n        self._idx += 1\n        return row\n\n    def _lazy_fetchone(self):\n        if not self._buffer:\n            if self._exhausted:\n                return None\n            rows = await_(self._fetch_many(self._buffer_size))\n            if not rows:\n                self._exhausted = True\n                return None\n            self._buffer.extend(rows)\n        return self._buffer.popleft()\n\n    def fetchall(self):\n        if self._fetch_many is not None:\n            return list(self)\n        return self._rows\n\n    def __iter__(self):\n        if self._fetch_many is not None:\n            return _lazy_cursor_iter(self)\n        return iter(self._rows)\n\n    def close(self):\n        pass\n\n    async def aclose(self):\n        if self._cleanup is not None:\n            try:\n                await self._cleanup()\n            finally:\n                self._cleanup = None\n                self._fetch_many = None\n\n\ndef _lazy_cursor_iter(cursor):\n    while True:\n        row = cursor.fetchone()\n        if row is None:\n            return\n        yield row\n\n\nclass DummyCursor(object):\n    def __init__(self, conn):\n        self.conn = conn\n\n    def execute(self, sql, params=None):\n        return await_(self._async_execute(sql, params))\n\n    async def _async_execute(self, sql, params):\n        return await self.conn.execute(sql, params)\n\n\nclass AsyncConnectionWrapper(object):\n    def __init__(self, conn):\n        self.conn = conn\n        self._lock = asyncio.Lock()\n\n    async def execute(self, sql, params=None):\n        async with self._lock:\n            return await self._execute(sql, params)\n\n    async def _execute(self, sql, params):\n        raise NotImplementedError('Subclasses must implement.')\n\n    def cursor(self):\n        return DummyCursor(self)\n\n    async def execute_iter(self, sql, params=None):\n        raise NotImplementedError('Subclasses must implement.')\n\n    async def close(self):\n        if self.conn:\n            await self.conn.close()\n            self.conn = None\n\n\nclass AsyncSqlitePool(object):\n    def __init__(self, database, pool_size=5, on_connect=None,\n                 **connect_params):\n        self._database = database\n        self._pool_size = pool_size\n        self._on_connect = on_connect\n        self._connect_params = connect_params\n        self._queue = asyncio.Queue(maxsize=pool_size)\n        self._all_connections = []\n        self._closed = False\n\n    async def initialize(self):\n        for _ in range(self._pool_size):\n            conn = await self._create_connection()\n            self._queue.put_nowait(conn)\n        return self\n\n    async def _create_connection(self):\n        conn = await aiosqlite.connect(\n            self._database,\n            isolation_level=None,\n            **self._connect_params)\n        if self._on_connect is not None:\n            await self._on_connect(conn )\n        wrapped = AsyncSqliteConnection(conn )\n        self._all_connections.append(wrapped)\n        return wrapped\n\n    async def acquire(self, timeout=None):\n        if self._closed:\n            raise InterfaceError('Pool is closed.')\n        return await asyncio.wait_for(self._queue.get(), timeout=timeout)\n\n    def _conn_is_valid(self, conn):\n        driver_conn = conn.conn\n        if driver_conn is None:\n            return False\n        if not driver_conn._running or not driver_conn._connection:\n            return False\n        return True\n\n    async def release(self, conn):\n        if self._closed:\n            return\n        elif self._conn_is_valid(conn):\n            await self._queue.put(conn)\n        else:\n            try:\n                self._all_connections.remove(conn)\n            except ValueError:\n                pass\n            await self._queue.put(await self._create_connection())\n\n    async def close(self):\n        self._closed = True\n        conns, self._all_connections = list(self._all_connections), []\n        for conn in conns:\n            try:\n                await conn.close()\n            except Exception:\n                logger.warning('Error closing pooled connection',\n                               exc_info=True)\n\n\nclass AsyncSqliteConnection(AsyncConnectionWrapper):\n    async def _execute(self, sql, params=None):\n        params = params or ()\n        cursor = await self.conn.execute(sql, params)\n        rows = await cursor.fetchall()\n        lastrowid = cursor.lastrowid\n        rowcount = cursor.rowcount\n        description = cursor.description\n        await cursor.close()\n        return CursorAdapter(rows, lastrowid=lastrowid, rowcount=rowcount,\n                             description=description)\n\n    async def execute_iter(self, sql, params=None):\n        await self._lock.acquire()\n        try:\n            cursor = await self.conn.execute(sql, params or ())\n        except BaseException:\n            self._lock.release()\n            raise\n\n        lock = self._lock\n\n        async def fetch_many(count):\n            return await cursor.fetchmany(count)\n\n        async def cleanup():\n            try:\n                await cursor.close()\n            finally:\n                lock.release()\n\n        return CursorAdapter(\n            description=cursor.description,\n            fetch_many=fetch_many,\n            cleanup=cleanup)\n\n\nclass AsyncSqliteDatabase(AsyncDatabaseMixin, SqliteDatabase):\n    async def _create_pool_async(self):\n        if aiosqlite is None:\n            raise ImproperlyConfigured('aiosqlite is not installed')\n        pool = AsyncSqlitePool(self.database, pool_size=self._pool_size,\n                               on_connect=self._add_conn_hooks)\n        return await pool.initialize()\n\n    async def _add_conn_hooks(self, conn):\n        if self._pragmas:\n            await self._set_pragmas(conn)\n        if self._functions:\n            await self._load_functions(conn)\n\n    async def _set_pragmas(self, conn):\n        for pragma, value in self._pragmas:\n            await conn.execute('PRAGMA %s = %s;' % (pragma, value))\n\n    async def _load_functions(self, conn):\n        for name, (fn, n_params, deterministic) in self._functions.items():\n            kwargs = {'deterministic': deterministic} if deterministic else {}\n            await conn.create_function(name, n_params, fn, **kwargs)\n\n    async def _pool_acquire(self):\n        return await self._pool.acquire(timeout=self._acquire_timeout)\n\n    async def _pool_release(self, conn):\n        if conn is not None:\n            await self._pool.release(conn)\n\n    async def _pool_close(self):\n        if self._pool:\n            await self._pool.close()\n\n\nclass AsyncMySQLConnection(AsyncConnectionWrapper):\n    async def _execute(self, sql, params=None):\n        params = params or ()\n        cursor = await self.conn.cursor()\n        try:\n            await cursor.execute(sql, params)\n            rows = await cursor.fetchall()\n            lastrowid = cursor.lastrowid\n            rowcount = cursor.rowcount\n            description = cursor.description\n        finally:\n            await cursor.close()\n        return CursorAdapter(rows, lastrowid=lastrowid, rowcount=rowcount,\n                             description=description)\n\n    async def execute_iter(self, sql, params=None):\n        await self._lock.acquire()\n        try:\n            # Server-side cursor for unbuffered streaming.\n            cursor = await self.conn.cursor(aiomysql.SSCursor)\n            await cursor.execute(sql, params or ())\n        except BaseException:\n            self._lock.release()\n            raise\n\n        lock = self._lock\n\n        async def fetch_many(count):\n            return await cursor.fetchmany(count)\n\n        async def cleanup():\n            try:\n                await cursor.close()\n            finally:\n                lock.release()\n\n        return CursorAdapter(\n            description=cursor.description,\n            fetch_many=fetch_many,\n            cleanup=cleanup)\n\n\nclass AsyncMySQLDatabase(AsyncDatabaseMixin, MySQLDatabase):\n    async def _create_pool_async(self):\n        if aiomysql is None:\n            raise ImproperlyConfigured('aiomysql is not installed')\n        return await aiomysql.create_pool(\n            db=self.database,\n            autocommit=True,\n            minsize=self._pool_min_size,\n            maxsize=self._pool_size,\n            **self.connect_params)\n\n    async def _pool_acquire(self):\n        conn = await asyncio.wait_for(\n            self._pool.acquire(),\n            timeout=self._acquire_timeout)\n        return AsyncMySQLConnection(conn)\n\n    async def _pool_release(self, conn):\n        if conn and conn.conn:\n            self._pool.release(conn.conn)\n\n    async def _pool_close(self):\n        self._pool.close()\n        await self._pool.wait_closed()\n\n\nclass AsyncPostgresqlConnection(AsyncConnectionWrapper):\n    async def _execute(self, sql, params=None):\n        # asyncpg uses $1, $2 positional params instead of %s.\n        if params:\n            sql = self._translate_placeholders(sql)\n\n        records = await self.conn.fetch(sql, *(params or ()))\n        if records:\n            description = [(k,) for k in records[0].keys()]\n            rows = records\n        else:\n            description = []\n            rows = []\n\n        return CursorAdapter(rows, description=description)\n\n    async def execute_iter(self, sql, params=None):\n        if params:\n            sql = self._translate_placeholders(sql)\n        await self._lock.acquire()\n        try:\n            # NB: asyncpg cursors require an active transaction.\n            # Right now we cannot use peewee-managed transactions because\n            # asyncpg's Cursor._check_ready() requires an asyncpg-managed\n            # transaction be active.\n            # See: https://github.com/MagicStack/asyncpg/issues/1311\n            tr = self.conn.transaction()\n            await tr.start()\n            stmt = await self.conn.prepare(sql)\n            cursor = await stmt.cursor(*(params or ()))\n        except BaseException:\n            self._lock.release()\n            raise\n\n        lock = self._lock\n\n        async def fetch_many(count):\n            return await cursor.fetch(count)\n\n        async def cleanup():\n            try:\n                await tr.rollback()\n            except:\n                pass\n            finally:\n                lock.release()\n\n        return CursorAdapter(\n            fetch_many=fetch_many,\n            cleanup=cleanup,\n            description=[(a.name,) for a in stmt.get_attributes()])\n\n    @staticmethod\n    def _translate_placeholders(sql):\n        parts = sql.split('%s')\n        if len(parts) == 1:\n            return sql\n        accum = [parts[0]]\n        for i, part in enumerate(parts[1:], 1):\n            accum.append('$%d' % i)\n            accum.append(part)\n        return ''.join(accum)\n\n\nclass AsyncPgAdapter(Psycopg3Adapter):\n    def __init__(self):\n        super(AsyncPgAdapter, self).__init__()\n        self.json_type = Json\n        self.jsonb_type = Json\n\n\nclass AsyncPgAtomic(object):\n    def __init__(self, db, *args, **kwargs):\n        self.db = db\n        self._begin_args = (args, kwargs)\n\n    def __enter__(self):\n        await_(self._abegin())\n        self.db._state.transactions.append(self)\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        self.db._state.transactions.pop()\n        if exc_type:\n            self.rollback(False)\n        else:\n            try:\n                self.commit(False)\n            except:\n                self.rollback(False)\n                raise\n\n    def commit(self, begin=True):\n        await_(self.acommit(begin))\n\n    def rollback(self, begin=True):\n        await_(self.arollback(begin))\n\n    async def _abegin(self):\n        a, k = self._begin_args\n        conn = await self.db.aconnect()\n        self._tx = conn.conn.transaction(*a, **k)\n        await self._tx.start()\n        return self._tx\n\n    async def acommit(self, begin=True):\n        await self._tx.commit()\n        if begin:\n            await self._abegin()\n\n    async def arollback(self, begin=True):\n        await self._tx.rollback()\n        if begin:\n            await self._abegin()\n\n    async def __aenter__(self):\n        await self._abegin()\n        self.db._state.transactions.append(self)\n        return self\n\n    async def __aexit__(self, exc_type, exc_val, exc_tb):\n        self.db._state.transactions.pop()\n        if exc_type:\n            await self.arollback(False)\n        else:\n            try:\n                await self.acommit(False)\n            except:\n                await self.arollback(False)\n                raise\n\n\nclass AsyncPostgresqlDatabase(AsyncDatabaseMixin, PostgresqlDatabase):\n    psycopg2_adapter = psycopg3_adapter = AsyncPgAdapter\n\n    async def register_adapters(self, conn):\n        def decode_json(bval):\n            return json.loads(bval.decode())\n\n        await conn.set_type_codec(\n            'json', encoder=str.encode, decoder=decode_json,\n            schema='pg_catalog', format='binary')\n\n        def encode_jsonb(val):\n            return b'\\x01' + val.encode('utf8')\n\n        def decode_jsonb(bval):\n            return json.loads(bval[1:].decode())\n\n        await conn.set_type_codec(\n            'jsonb', encoder=encode_jsonb, decoder=decode_jsonb,\n            schema='pg_catalog', format='binary')\n\n    async def _create_pool_async(self):\n        if asyncpg is None:\n            raise ImproperlyConfigured('asyncpg is not installed')\n        return await asyncpg.create_pool(\n            database=self.database,\n            min_size=self._pool_min_size,\n            max_size=self._pool_size,\n            init=self.register_adapters,\n            **self.connect_params)\n\n    async def _pool_acquire(self):\n        conn = await asyncio.wait_for(\n            self._pool.acquire(),\n            timeout=self._acquire_timeout)\n        return AsyncPostgresqlConnection(conn)\n\n    async def _pool_release(self, conn):\n        if conn and conn.conn:\n            await self._pool.release(conn.conn)\n\n    async def _pool_close(self):\n        await self._pool.close()\n\n    def atomic(self, *args, **kwargs):\n        return AsyncPgAtomic(self, *args, **kwargs)\n    def transaction(self, *args, **kwargs):\n        return AsyncPgAtomic(self, *args, **kwargs)\n    def savepoint(self, *args, **kwargs):\n        return AsyncPgAtomic(self, *args, **kwargs)\n"
  },
  {
    "path": "playhouse/pydantic_utils.py",
    "content": "from __future__ import annotations\n\nfrom typing import Any\nfrom typing import Literal\nfrom typing import Optional\nfrom typing import get_origin\n\nfrom peewee import AutoField\nfrom peewee import ForeignKeyField\nfrom peewee import Model\nfrom playhouse.reflection import FieldTypeMap\n\nfrom pydantic import BaseModel\nfrom pydantic import ConfigDict\nfrom pydantic import Field\nfrom pydantic import create_model\n\n\ndef choices_to_literal(choices):\n    return Literal[tuple(val for val, label in choices)]\n\ndef choices_description(choices):\n    return ', '.join(['%r = %s' % (value, label) for value, label in choices])\n\ndef get_field_type(field):\n    if isinstance(field, ForeignKeyField):\n        field = field.rel_field\n    return FieldTypeMap.get(field.field_type, Any)\n\ndef to_pydantic(model_cls, exclude=None, include=None, exclude_autofield=True,\n                model_name=None, relationships=None):\n    exclude = exclude or set()\n    relationships = relationships or {}\n    fields = {}\n\n    rel_fields = {}\n    backref_fields = {}\n    for field, schema in relationships.items():\n        if isinstance(field, ForeignKeyField):\n            rel_fields[field.name] = schema\n        else:\n            backref_fields[field.field.backref] = schema\n\n    for field in model_cls._meta.sorted_fields:\n        name = field.name\n        if name in exclude:\n            continue\n        elif include is not None and name not in include:\n            continue\n        elif exclude_autofield and isinstance(field, AutoField):\n            continue\n\n        if isinstance(field, ForeignKeyField):\n            if name in rel_fields:\n                schema = rel_fields[name]\n                field_kwargs = {}\n                if field.verbose_name:\n                    field_kwargs['title'] = field.verbose_name\n                if field.help_text:\n                    field_kwargs['description'] = field.help_text\n                if field.null:\n                    schema = Optional[schema]\n                    field_kwargs['default'] = None\n                fields[name] = (schema, Field(**field_kwargs))\n                continue\n\n            name = field.column_name\n\n        python_type = get_field_type(field)\n        choices = field.choices\n        if choices:\n            python_type = choices_to_literal(choices)\n\n        parts = []\n        if field.help_text:\n            parts.append(field.help_text)\n        if choices:\n            parts.append('Choices: %s' % choices_description(choices))\n        description = ' | '.join(parts) or None\n\n        field_kwargs = {}\n        if field.verbose_name:\n            field_kwargs['title'] = field.verbose_name\n        if description:\n            field_kwargs['description'] = description\n\n        if field.default is not None:\n            if callable(field.default):\n                field_kwargs['default_factory'] = field.default\n            else:\n                field_kwargs['default'] = field.default\n            if field.null:\n                python_type = Optional[python_type]\n        elif field.null:\n            python_type = Optional[python_type]\n            field_kwargs['default'] = None\n\n        fields[name] = (python_type, Field(**field_kwargs))\n\n    for name, schema in backref_fields.items():\n        origin = get_origin(schema)\n        if origin is not list:\n            raise ValueError('back-references must use a List type')\n        fields[name] = (schema, Field(default_factory=list))\n\n    model_name = model_name or ('%sSchema' % model_cls.__name__)\n\n    return create_model(\n        model_name,\n        __config__=ConfigDict(from_attributes=True),\n        **fields)\n"
  },
  {
    "path": "playhouse/reflection.py",
    "content": "import datetime\nimport decimal\nimport re\nimport uuid\nimport warnings\nfrom collections import OrderedDict\nfrom collections import namedtuple\nfrom inspect import isclass\n\nfrom peewee import *\nfrom peewee import _StringField\nfrom peewee import _query_val_transform\nfrom peewee import CommaNodeList\nfrom peewee import SCOPE_VALUES\nfrom peewee import make_snake_case\ntry:\n    from pymysql.constants import FIELD_TYPE\nexcept ImportError:\n    try:\n        from MySQLdb.constants import FIELD_TYPE\n    except ImportError:\n        FIELD_TYPE = None\ntry:\n    from playhouse import postgres_ext\nexcept ImportError:\n    postgres_ext = None\ntry:\n    from playhouse.cockroachdb import CockroachDatabase\nexcept ImportError:\n    CockroachDatabase = None\n\nRESERVED_WORDS = set([\n    'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif',\n    'else', 'except', 'exec', 'finally', 'for', 'from', 'global', 'if',\n    'import', 'in', 'is', 'lambda', 'not', 'or', 'pass', 'print', 'raise',\n    'return', 'try', 'while', 'with', 'yield',\n])\n\n\nFieldTypeMap = {\n    'AUTO': int,\n    'BIGAUTO': int,\n    'BIGINT': int,\n    'BLOB': bytes,\n    'BOOL': bool,\n    'CHAR': str,\n    'DATE': datetime.date,\n    'DATETIME': datetime.datetime,\n    'DECIMAL': decimal.Decimal,\n    'DOUBLE': float,\n    'FLOAT': float,\n    'INT': int,\n    'SMALLINT': int,\n    'TEXT': str,\n    'TIME': datetime.time,\n    'UUID': uuid.UUID,\n    'UUIDB': bytes,\n    'VARCHAR': str,\n    'JSON': dict,\n    'JSONB': dict,\n    'TIMESTAMPTZ': datetime.datetime,\n    'INTERVAL': datetime.timedelta,\n}\n\n\nclass UnknownField(object):\n    pass\n\n\nclass Column(object):\n    \"\"\"\n    Store metadata about a database column.\n    \"\"\"\n    primary_key_types = (IntegerField, AutoField)\n\n    def __init__(self, name, field_class, raw_column_type, nullable,\n                 primary_key=False, column_name=None, index=False,\n                 unique=False, default=None, extra_parameters=None):\n        self.name = name\n        self.field_class = field_class\n        self.raw_column_type = raw_column_type\n        self.nullable = nullable\n        self.primary_key = primary_key\n        self.column_name = column_name\n        self.index = index\n        self.unique = unique\n        self.default = default\n        self.extra_parameters = extra_parameters\n\n        # Foreign key metadata.\n        self.rel_model = None\n        self.related_name = None\n        self.to_field = None\n\n    def __repr__(self):\n        attrs = [\n            'field_class',\n            'raw_column_type',\n            'nullable',\n            'primary_key',\n            'column_name']\n        keyword_args = ', '.join(\n            '%s=%s' % (attr, getattr(self, attr))\n            for attr in attrs)\n        return 'Column(%s, %s)' % (self.name, keyword_args)\n\n    def get_field_parameters(self):\n        params = {}\n        if self.extra_parameters is not None:\n            params.update(self.extra_parameters)\n\n        # Set up default attributes.\n        if self.nullable:\n            params['null'] = True\n        if self.field_class is ForeignKeyField or self.name != self.column_name:\n            params['column_name'] = \"'%s'\" % self.column_name\n        if self.primary_key and not issubclass(self.field_class, AutoField):\n            params['primary_key'] = True\n        if self.default is not None:\n            params['constraints'] = '[SQL(\"DEFAULT %s\")]' % \\\n                    self.default.replace('\"', '\\\\\"')\n\n        # Handle ForeignKeyField-specific attributes.\n        if self.is_foreign_key():\n            params['model'] = self.rel_model\n            if self.to_field:\n                params['field'] = \"'%s'\" % self.to_field\n            if self.related_name:\n                params['backref'] = \"'%s'\" % self.related_name\n\n        # Handle indexes on column.\n        if not self.is_primary_key():\n            if self.unique:\n                params['unique'] = 'True'\n            elif self.index and not self.is_foreign_key():\n                params['index'] = 'True'\n\n        return params\n\n    def is_primary_key(self):\n        return self.field_class is AutoField or self.primary_key\n\n    def is_foreign_key(self):\n        return self.field_class is ForeignKeyField\n\n    def is_self_referential_fk(self):\n        return (self.field_class is ForeignKeyField and\n                self.rel_model == \"'self'\")\n\n    def set_foreign_key(self, foreign_key, model_names, dest=None,\n                        related_name=None):\n        self.foreign_key = foreign_key\n        self.field_class = ForeignKeyField\n        if foreign_key.dest_table == foreign_key.table:\n            self.rel_model = \"'self'\"\n        else:\n            self.rel_model = model_names[foreign_key.dest_table]\n        self.to_field = dest and dest.name or None\n        self.related_name = related_name or None\n\n    def get_field(self):\n        # Generate the field definition for this column.\n        field_params = {}\n        for key, value in self.get_field_parameters().items():\n            if isclass(value) and issubclass(value, Field):\n                value = value.__name__\n            field_params[key] = value\n\n        param_str = ', '.join('%s=%s' % (k, v)\n                              for k, v in sorted(field_params.items()))\n        field = '%s = %s(%s)' % (\n            self.name,\n            self.field_class.__name__,\n            param_str)\n\n        if self.field_class is UnknownField:\n            field = '%s  # %s' % (field, self.raw_column_type)\n\n        return field\n\n\nclass Metadata(object):\n    column_map = {}\n    extension_import = ''\n\n    def __init__(self, database):\n        self.database = database\n        self.requires_extension = False\n\n    def execute(self, sql, *params):\n        return self.database.execute_sql(sql, params)\n\n    def get_columns(self, table, schema=None):\n        metadata = OrderedDict(\n            (metadata.name, metadata)\n            for metadata in self.database.get_columns(table, schema))\n\n        # Look up the actual column type for each column.\n        column_types, extra_params = self.get_column_types(table, schema)\n\n        # Look up the primary keys.\n        pk_names = self.get_primary_keys(table, schema)\n        if len(pk_names) == 1:\n            pk = pk_names[0]\n            if column_types[pk] is IntegerField:\n                column_types[pk] = AutoField\n            elif column_types[pk] is BigIntegerField:\n                column_types[pk] = BigAutoField\n\n        columns = OrderedDict()\n        for name, column_data in metadata.items():\n            field_class = column_types[name]\n            default = self._clean_default(field_class, column_data.default)\n\n            columns[name] = Column(\n                name,\n                field_class=field_class,\n                raw_column_type=column_data.data_type,\n                nullable=column_data.null,\n                primary_key=column_data.primary_key,\n                column_name=name,\n                default=default,\n                extra_parameters=extra_params.get(name))\n\n        return columns\n\n    def get_column_types(self, table, schema=None):\n        raise NotImplementedError\n\n    def _clean_default(self, field_class, default):\n        if default is None or field_class in (AutoField, BigAutoField) or \\\n           default.lower() == 'null':\n            return\n        if issubclass(field_class, _StringField) and \\\n           isinstance(default, str) and not default.startswith(\"'\"):\n            default = \"'%s'\" % default\n        return default or \"''\"\n\n    def get_foreign_keys(self, table, schema=None):\n        return self.database.get_foreign_keys(table, schema)\n\n    def get_primary_keys(self, table, schema=None):\n        return self.database.get_primary_keys(table, schema)\n\n    def get_indexes(self, table, schema=None):\n        return self.database.get_indexes(table, schema)\n\n\nclass PostgresqlMetadata(Metadata):\n    column_map = {\n        16: BooleanField,\n        17: BlobField,\n        20: BigIntegerField,\n        21: SmallIntegerField,\n        23: IntegerField,\n        25: TextField,\n        700: FloatField,\n        701: DoubleField,\n        1042: CharField, # blank-padded CHAR\n        1043: CharField,\n        1082: DateField,\n        1114: DateTimeField,\n        1184: DateTimeField,\n        1083: TimeField,\n        1266: TimeField,\n        1700: DecimalField,\n        2950: UUIDField, # UUID\n    }\n    array_types = {\n        1000: BooleanField,\n        1001: BlobField,\n        1005: SmallIntegerField,\n        1007: IntegerField,\n        1009: TextField,\n        1014: CharField,\n        1015: CharField,\n        1016: BigIntegerField,\n        1115: DateTimeField,\n        1182: DateField,\n        1183: TimeField,\n        2951: UUIDField,\n    }\n    extension_import = 'from playhouse.postgres_ext import *'\n\n    def __init__(self, database):\n        super(PostgresqlMetadata, self).__init__(database)\n\n        if postgres_ext is not None:\n            # Attempt to add types like HStore and JSON.\n            cursor = self.execute('select oid, typname, format_type(oid, NULL)'\n                                  ' from pg_type;')\n            results = cursor.fetchall()\n\n            for oid, typname, formatted_type in results:\n                if typname == 'json':\n                    self.column_map[oid] = postgres_ext.JSONField\n                elif typname == 'jsonb':\n                    self.column_map[oid] = postgres_ext.BinaryJSONField\n                elif typname == 'hstore':\n                    self.column_map[oid] = postgres_ext.HStoreField\n                elif typname == 'tsvector':\n                    self.column_map[oid] = postgres_ext.TSVectorField\n\n            for oid in self.array_types:\n                self.column_map[oid] = postgres_ext.ArrayField\n\n    def get_column_types(self, table, schema):\n        column_types = {}\n        extra_params = {}\n        extension_types = set((\n            postgres_ext.ArrayField,\n            postgres_ext.BinaryJSONField,\n            postgres_ext.JSONField,\n            postgres_ext.TSVectorField,\n            postgres_ext.HStoreField)) if postgres_ext is not None else set()\n\n        # Look up the actual column type for each column.\n        identifier = '%s.\"%s\"' % (schema, table)\n        cursor = self.execute(\n            'SELECT attname, atttypid FROM pg_catalog.pg_attribute '\n            'WHERE attrelid = %s::regclass AND attnum > %s', identifier, 0)\n\n        # Store column metadata in dictionary keyed by column name.\n        for name, oid in cursor.fetchall():\n            column_types[name] = self.column_map.get(oid, UnknownField)\n            if column_types[name] in extension_types:\n                self.requires_extension = True\n            if oid in self.array_types:\n                extra_params[name] = {'field_class': self.array_types[oid]}\n\n        return column_types, extra_params\n\n    def get_columns(self, table, schema=None):\n        schema = schema or 'public'\n        return super(PostgresqlMetadata, self).get_columns(table, schema)\n\n    def get_foreign_keys(self, table, schema=None):\n        schema = schema or 'public'\n        return super(PostgresqlMetadata, self).get_foreign_keys(table, schema)\n\n    def get_primary_keys(self, table, schema=None):\n        schema = schema or 'public'\n        return super(PostgresqlMetadata, self).get_primary_keys(table, schema)\n\n    def get_indexes(self, table, schema=None):\n        schema = schema or 'public'\n        return super(PostgresqlMetadata, self).get_indexes(table, schema)\n\n\nclass CockroachDBMetadata(PostgresqlMetadata):\n    # CRDB treats INT the same as BIGINT, so we just map bigint type OIDs to\n    # regular IntegerField.\n    column_map = PostgresqlMetadata.column_map.copy()\n    column_map[20] = IntegerField\n    array_types = PostgresqlMetadata.array_types.copy()\n    array_types[1016] = IntegerField\n    extension_import = 'from playhouse.cockroachdb import *'\n\n    def __init__(self, database):\n        Metadata.__init__(self, database)\n        self.requires_extension = True\n\n        if postgres_ext is not None:\n            # Attempt to add JSON types.\n            cursor = self.execute('select oid, typname, format_type(oid, NULL)'\n                                  ' from pg_type;')\n            results = cursor.fetchall()\n\n            for oid, typname, formatted_type in results:\n                if typname == 'jsonb':\n                    self.column_map[oid] = postgres_ext.BinaryJSONField\n\n            for oid in self.array_types:\n                self.column_map[oid] = postgres_ext.ArrayField\n\n\nclass MySQLMetadata(Metadata):\n    if FIELD_TYPE is None:\n        column_map = {}\n    else:\n        column_map = {\n            FIELD_TYPE.BLOB: TextField,\n            FIELD_TYPE.CHAR: CharField,\n            FIELD_TYPE.DATE: DateField,\n            FIELD_TYPE.DATETIME: DateTimeField,\n            FIELD_TYPE.DECIMAL: DecimalField,\n            FIELD_TYPE.DOUBLE: FloatField,\n            FIELD_TYPE.FLOAT: FloatField,\n            FIELD_TYPE.INT24: IntegerField,\n            FIELD_TYPE.LONG_BLOB: TextField,\n            FIELD_TYPE.LONG: IntegerField,\n            FIELD_TYPE.LONGLONG: BigIntegerField,\n            FIELD_TYPE.MEDIUM_BLOB: TextField,\n            FIELD_TYPE.NEWDECIMAL: DecimalField,\n            FIELD_TYPE.SHORT: IntegerField,\n            FIELD_TYPE.STRING: CharField,\n            FIELD_TYPE.TIMESTAMP: DateTimeField,\n            FIELD_TYPE.TIME: TimeField,\n            FIELD_TYPE.TINY_BLOB: TextField,\n            FIELD_TYPE.TINY: IntegerField,\n            FIELD_TYPE.VAR_STRING: CharField,\n        }\n\n    def __init__(self, database, **kwargs):\n        if 'password' in kwargs:\n            kwargs['passwd'] = kwargs.pop('password')\n        super(MySQLMetadata, self).__init__(database, **kwargs)\n\n    def get_column_types(self, table, schema=None):\n        column_types = {}\n\n        # Look up the actual column type for each column.\n        cursor = self.execute('SELECT * FROM `%s` LIMIT 1' % table)\n\n        # Store column metadata in dictionary keyed by column name.\n        for column_description in cursor.description:\n            name, type_code = column_description[:2]\n            column_types[name] = self.column_map.get(type_code, UnknownField)\n\n        return column_types, {}\n\n\nclass SqliteMetadata(Metadata):\n    column_map = {\n        'bigint': BigIntegerField,\n        'blob': BlobField,\n        'bool': BooleanField,\n        'boolean': BooleanField,\n        'char': CharField,\n        'date': DateField,\n        'datetime': DateTimeField,\n        'decimal': DecimalField,\n        'float': FloatField,\n        'integer': IntegerField,\n        'integer unsigned': IntegerField,\n        'int': IntegerField,\n        'long': BigIntegerField,\n        'numeric': DecimalField,\n        'real': FloatField,\n        'smallinteger': IntegerField,\n        'smallint': IntegerField,\n        'smallint unsigned': IntegerField,\n        'text': TextField,\n        'time': TimeField,\n        'varchar': CharField,\n    }\n\n    begin = r'(?:[\"\\[\\(]+)?'\n    end = r'(?:[\"\\]\\)]+)?'\n    re_foreign_key = (\n        r'(?:FOREIGN KEY\\s*)?'\n        r'{begin}(.+?){end}\\s+(?:.+\\s+)?'\n        r'references\\s+{begin}(.+?){end}'\n        r'\\s*\\([\"|\\[]?(.+?)[\"|\\]]?\\)').format(begin=begin, end=end)\n    re_varchar = r'^\\s*(?:var)?char\\s*\\(\\s*(\\d+)\\s*\\)\\s*$'\n\n    def _map_col(self, column_type):\n        raw_column_type = column_type.lower()\n        if raw_column_type in self.column_map:\n            field_class = self.column_map[raw_column_type]\n        elif re.search(self.re_varchar, raw_column_type):\n            field_class = CharField\n        else:\n            column_type = re.sub(r'\\(.+\\)', '', raw_column_type)\n            if column_type == '':\n                field_class = BareField\n            else:\n                field_class = self.column_map.get(column_type, UnknownField)\n        return field_class\n\n    def get_column_types(self, table, schema=None):\n        column_types = {}\n        columns = self.database.get_columns(table)\n\n        for column in columns:\n            column_types[column.name] = self._map_col(column.data_type)\n\n        return column_types, {}\n\n\n_DatabaseMetadata = namedtuple('_DatabaseMetadata', (\n    'columns',\n    'primary_keys',\n    'foreign_keys',\n    'model_names',\n    'indexes'))\n\n\nclass DatabaseMetadata(_DatabaseMetadata):\n    def multi_column_indexes(self, table):\n        accum = []\n        for index in self.indexes[table]:\n            if len(index.columns) > 1:\n                field_names = [self.columns[table][column].name\n                               for column in index.columns\n                               if column in self.columns[table]]\n                accum.append((field_names, index.unique))\n        return accum\n\n    def column_indexes(self, table):\n        accum = {}\n        for index in self.indexes[table]:\n            if len(index.columns) == 1:\n                accum[index.columns[0]] = index.unique\n        return accum\n\n\nclass Introspector(object):\n    pk_classes = [AutoField, IntegerField]\n\n    def __init__(self, metadata, schema=None):\n        self.metadata = metadata\n        self.schema = schema\n\n    def __repr__(self):\n        return '<Introspector: %s>' % self.metadata.database\n\n    @classmethod\n    def from_database(cls, database, schema=None):\n        if isinstance(database, Proxy):\n            if database.obj is None:\n                raise ValueError('Cannot introspect an uninitialized Proxy.')\n            database = database.obj  # Reference the proxied db obj.\n        if CockroachDatabase and isinstance(database, CockroachDatabase):\n            metadata = CockroachDBMetadata(database)\n        elif isinstance(database, PostgresqlDatabase):\n            metadata = PostgresqlMetadata(database)\n        elif isinstance(database, MySQLDatabase):\n            metadata = MySQLMetadata(database)\n        elif isinstance(database, SqliteDatabase):\n            metadata = SqliteMetadata(database)\n        else:\n            raise ValueError('Introspection not supported for %r' % database)\n        return cls(metadata, schema=schema)\n\n    def get_database_class(self):\n        return type(self.metadata.database)\n\n    def get_database_name(self):\n        return self.metadata.database.database\n\n    def get_database_kwargs(self):\n        return self.metadata.database.connect_params\n\n    def get_additional_imports(self):\n        if self.metadata.requires_extension:\n            return '\\n' + self.metadata.extension_import\n        return ''\n\n    def make_model_name(self, table, snake_case=True):\n        if snake_case:\n            table = make_snake_case(table)\n        model = re.sub(r'[^\\w]+', '', table)\n        model_name = ''.join(sub.title() for sub in model.split('_'))\n        if not model_name[0].isalpha():\n            model_name = 'T' + model_name\n        return model_name\n\n    def make_column_name(self, column, is_foreign_key=False, snake_case=True):\n        column = column.strip()\n        if snake_case:\n            column = make_snake_case(column)\n        column = column.lower()\n        if is_foreign_key:\n            # Strip \"_id\" from foreign keys, unless the foreign-key happens to\n            # be named \"_id\", in which case the name is retained.\n            column = re.sub('_id$', '', column) or column\n\n        # Remove characters that are invalid for Python identifiers.\n        column = re.sub(r'[^\\w]+', '_', column)\n        if column in RESERVED_WORDS:\n            column += '_'\n        if len(column) and column[0].isdigit():\n            column = '_' + column\n        return column\n\n    def introspect(self, table_names=None, literal_column_names=False,\n                   include_views=False, snake_case=True):\n        # Retrieve all the tables in the database.\n        tables = self.metadata.database.get_tables(schema=self.schema)\n        if include_views:\n            views = self.metadata.database.get_views(schema=self.schema)\n            tables.extend([view.name for view in views])\n\n        if table_names is not None:\n            tables = [table for table in tables if table in table_names]\n        table_set = set(tables)\n\n        # Store a mapping of table name -> dictionary of columns.\n        columns = {}\n\n        # Store a mapping of table name -> set of primary key columns.\n        primary_keys = {}\n\n        # Store a mapping of table -> foreign keys.\n        foreign_keys = {}\n\n        # Store a mapping of table name -> model name.\n        model_names = {}\n\n        # Store a mapping of table name -> indexes.\n        indexes = {}\n\n        # Gather the columns for each table.\n        for table in tables:\n            table_indexes = self.metadata.get_indexes(table, self.schema)\n            table_columns = self.metadata.get_columns(table, self.schema)\n            try:\n                foreign_keys[table] = self.metadata.get_foreign_keys(\n                    table, self.schema)\n            except ValueError as exc:\n                foreign_keys[table] = []\n            else:\n                # If there is a possibility we could exclude a dependent table,\n                # ensure that we introspect it so FKs will work.\n                if table_names is not None:\n                    for foreign_key in foreign_keys[table]:\n                        if foreign_key.dest_table not in table_set:\n                            tables.append(foreign_key.dest_table)\n                            table_set.add(foreign_key.dest_table)\n\n            model_names[table] = self.make_model_name(table, snake_case)\n\n            # Collect sets of all the column names as well as all the\n            # foreign-key column names.\n            lower_col_names = set(column_name.lower()\n                                  for column_name in table_columns)\n            fks = set(fk_col.column for fk_col in foreign_keys[table])\n\n            for col_name, column in table_columns.items():\n                if literal_column_names:\n                    new_name = re.sub(r'[^\\w]+', '_', col_name)\n                else:\n                    new_name = self.make_column_name(col_name, col_name in fks,\n                                                     snake_case)\n\n                # If we have two columns, \"parent\" and \"parent_id\", ensure\n                # that when we don't introduce naming conflicts.\n                lower_name = col_name.lower()\n                if lower_name.endswith('_id') and new_name in lower_col_names:\n                    new_name = col_name.lower()\n\n                column.name = new_name\n\n            for index in table_indexes:\n                if len(index.columns) == 1:\n                    column = index.columns[0]\n                    if column in table_columns:\n                        table_columns[column].unique = index.unique\n                        table_columns[column].index = True\n\n            primary_keys[table] = self.metadata.get_primary_keys(\n                table, self.schema)\n            columns[table] = table_columns\n            indexes[table] = table_indexes\n\n        # Gather all instances where we might have a `related_name` conflict,\n        # either due to multiple FKs on a table pointing to the same table,\n        # or a related_name that would conflict with an existing field.\n        related_names = {}\n        sort_fn = lambda foreign_key: foreign_key.column\n        for table in tables:\n            models_referenced = set()\n            for foreign_key in sorted(foreign_keys[table], key=sort_fn):\n                try:\n                    column = columns[table][foreign_key.column]\n                except KeyError:\n                    continue\n\n                dest_table = foreign_key.dest_table\n                if dest_table in models_referenced:\n                    related_names[column] = '%s_%s_set' % (\n                        dest_table,\n                        column.name)\n                else:\n                    models_referenced.add(dest_table)\n\n        # On the second pass convert all foreign keys.\n        for table in tables:\n            for foreign_key in foreign_keys[table]:\n                src = columns[foreign_key.table][foreign_key.column]\n                try:\n                    dest = columns[foreign_key.dest_table][\n                        foreign_key.dest_column]\n                except KeyError:\n                    dest = None\n\n                src.set_foreign_key(\n                    foreign_key=foreign_key,\n                    model_names=model_names,\n                    dest=dest,\n                    related_name=related_names.get(src))\n\n        return DatabaseMetadata(\n            columns,\n            primary_keys,\n            foreign_keys,\n            model_names,\n            indexes)\n\n    def generate_models(self, skip_invalid=False, table_names=None,\n                        literal_column_names=False, bare_fields=False,\n                        include_views=False):\n        database = self.introspect(table_names, literal_column_names,\n                                   include_views)\n        models = {}\n\n        class BaseModel(Model):\n            class Meta:\n                database = self.metadata.database\n                schema = self.schema\n\n        pending = set()\n\n        def _create_model(table, models):\n            pending.add(table)\n            for foreign_key in database.foreign_keys[table]:\n                dest = foreign_key.dest_table\n\n                if dest not in models and dest != table:\n                    if dest in pending:\n                        warnings.warn('Possible reference cycle found between '\n                                      '%s and %s' % (table, dest))\n                    else:\n                        _create_model(dest, models)\n\n            primary_keys = []\n            columns = database.columns[table]\n            for column_name, column in columns.items():\n                if column.primary_key:\n                    primary_keys.append(column.name)\n\n            multi_column_indexes = database.multi_column_indexes(table)\n            column_indexes = database.column_indexes(table)\n\n            class Meta:\n                indexes = multi_column_indexes\n                table_name = table\n\n            # Fix models with multi-column primary keys.\n            composite_key = False\n            if len(primary_keys) == 0:\n                if 'id' not in columns:\n                    Meta.primary_key = False\n                else:\n                    primary_keys = columns.keys()\n\n            if len(primary_keys) > 1:\n                Meta.primary_key = CompositeKey(*[\n                    field.name for col, field in columns.items()\n                    if col in primary_keys])\n                composite_key = True\n\n            attrs = {'Meta': Meta}\n            for column_name, column in columns.items():\n                FieldClass = column.field_class\n                if FieldClass is not ForeignKeyField and bare_fields:\n                    FieldClass = BareField\n                elif FieldClass is UnknownField:\n                    FieldClass = BareField\n\n                params = {\n                    'column_name': column_name,\n                    'null': column.nullable}\n                if column.primary_key and composite_key:\n                    if FieldClass is AutoField:\n                        FieldClass = IntegerField\n                    params['primary_key'] = False\n                elif column.primary_key and FieldClass is not AutoField:\n                    params['primary_key'] = True\n                if column.is_foreign_key():\n                    if column.is_self_referential_fk():\n                        params['model'] = 'self'\n                    else:\n                        dest_table = column.foreign_key.dest_table\n                        if dest_table in models:\n                            params['model'] = models[dest_table]\n                        else:\n                            FieldClass = DeferredForeignKey\n                            params['rel_model_name'] = dest_table\n                    if column.to_field:\n                        params['field'] = column.to_field\n\n                    # Generate a unique related name.\n                    params['backref'] = '%s_%s_rel' % (table, column_name)\n\n                if column.default is not None:\n                    constraint = SQL('DEFAULT %s' % column.default)\n                    params['constraints'] = [constraint]\n\n                if not column.is_primary_key():\n                    if column_name in column_indexes:\n                        if column_indexes[column_name]:\n                            params['unique'] = True\n                        elif not column.is_foreign_key():\n                            params['index'] = True\n                    else:\n                        params['index'] = False\n\n                attrs[column.name] = FieldClass(**params)\n\n            try:\n                models[table] = type(str(table), (BaseModel,), attrs)\n            except ValueError:\n                if not skip_invalid:\n                    raise\n            finally:\n                if table in pending:\n                    pending.remove(table)\n\n        # Actually generate Model classes.\n        for table, model in sorted(database.model_names.items()):\n            if table not in models:\n                _create_model(table, models)\n\n        return models\n\n\ndef introspect(database, schema=None):\n    introspector = Introspector.from_database(database, schema=schema)\n    return introspector.introspect()\n\n\ndef generate_models(database, schema=None, **options):\n    introspector = Introspector.from_database(database, schema=schema)\n    return introspector.generate_models(**options)\n\n\ndef print_model(model, indexes=True, inline_indexes=False):\n    print(model._meta.name)\n    for field in model._meta.sorted_fields:\n        parts = ['  %s %s' % (field.name, field.field_type)]\n        if field.primary_key:\n            parts.append(' PK')\n        elif inline_indexes:\n            if field.unique:\n                parts.append(' UNIQUE')\n            elif field.index:\n                parts.append(' INDEX')\n        if isinstance(field, ForeignKeyField):\n            parts.append(' FK: %s.%s' % (field.rel_model.__name__,\n                                         field.rel_field.name))\n        print(''.join(parts))\n\n    if indexes:\n        index_list = model._meta.fields_to_index()\n        if not index_list:\n            return\n\n        print('\\nindex(es)')\n        for index in index_list:\n            parts = ['  ']\n            ctx = model._meta.database.get_sql_context()\n            with ctx.scope_values(param='%s', quote='\"\"'):\n                ctx.sql(CommaNodeList(index._expressions))\n                if index._where:\n                    ctx.literal(' WHERE ')\n                    ctx.sql(index._where)\n                sql, params = ctx.query()\n\n            clean = sql % tuple(map(_query_val_transform, params))\n            parts.append(clean.replace('\"', ''))\n\n            if index._unique:\n                parts.append(' UNIQUE')\n            print(''.join(parts))\n\n\ndef get_table_sql(model):\n    sql, params = model._schema._create_table().query()\n    if model._meta.database.param != '%s':\n        sql = sql.replace(model._meta.database.param, '%s')\n\n    # Format and indent the table declaration, simplest possible approach.\n    match_obj = re.match(r'^(.+?\\()(.+)(\\).*)', sql)\n    create, columns, extra = match_obj.groups()\n    indented = ',\\n'.join('  %s' % column for column in columns.split(', '))\n\n    clean = '\\n'.join((create, indented, extra)).strip()\n    return clean % tuple(map(_query_val_transform, params))\n\ndef print_table_sql(model):\n    print(get_table_sql(model))\n"
  },
  {
    "path": "playhouse/shortcuts.py",
    "content": "import threading\n\nfrom peewee import *\nfrom peewee import Alias\nfrom peewee import CompoundSelectQuery\nfrom peewee import Metadata\nfrom peewee import callable_\n\n\n_clone_set = lambda s: set(s) if s else set()\n\n\ndef model_to_dict(model, recurse=True, backrefs=False, only=None,\n                  exclude=None, seen=None, extra_attrs=None,\n                  fields_from_query=None, max_depth=None, manytomany=False):\n    \"\"\"\n    Convert a model instance (and any related objects) to a dictionary.\n\n    :param bool recurse: Whether foreign-keys should be recursed.\n    :param bool backrefs: Whether lists of related objects should be recursed.\n    :param only: A list (or set) of field instances indicating which fields\n        should be included.\n    :param exclude: A list (or set) of field instances that should be\n        excluded from the dictionary.\n    :param list extra_attrs: Names of model instance attributes or methods\n        that should be included.\n    :param SelectQuery fields_from_query: Query that was source of model. Take\n        fields explicitly selected by the query and serialize them.\n    :param int max_depth: Maximum depth to recurse, value <= 0 means no max.\n    :param bool manytomany: Process many-to-many fields.\n    \"\"\"\n    max_depth = -1 if max_depth is None else max_depth\n    if max_depth == 0:\n        recurse = False\n\n    only = _clone_set(only)\n    extra_attrs = _clone_set(extra_attrs)\n    should_skip = lambda n: (n in exclude) or (only and (n not in only))\n\n    if fields_from_query is not None:\n        only.add('__sentinel__')  # Add a placeholder to make non-empty.\n        for item in fields_from_query._returning:\n            if isinstance(item, Field):\n                only.add(item)\n            elif isinstance(item, Alias):\n                extra_attrs.add(item._alias)\n\n    data = {}\n    exclude = _clone_set(exclude)\n    seen = _clone_set(seen)\n    exclude |= seen\n    model_class = type(model)\n\n    if manytomany:\n        for name, m2m in model._meta.manytomany.items():\n            if should_skip(name):\n                continue\n\n            exclude.update((m2m, m2m.rel_model._meta.manytomany[m2m.backref]))\n            for fkf in m2m.through_model._meta.refs:\n                exclude.add(fkf)\n\n            accum = []\n            for rel_obj in getattr(model, name):\n                accum.append(model_to_dict(\n                    rel_obj,\n                    recurse=recurse,\n                    backrefs=backrefs,\n                    only=only,\n                    exclude=exclude,\n                    max_depth=max_depth - 1))\n            data[name] = accum\n\n    for field in model._meta.sorted_fields:\n        if should_skip(field):\n            continue\n\n        field_data = model.__data__.get(field.name)\n        if isinstance(field, ForeignKeyField) and recurse:\n            if field_data is not None:\n                rel_obj = getattr(model, field.name)\n                field_data = model_to_dict(\n                    rel_obj,\n                    recurse=recurse,\n                    backrefs=backrefs,\n                    only=only,\n                    exclude=exclude,\n                    seen=seen | set((field,)),\n                    max_depth=max_depth - 1)\n            else:\n                field_data = None\n\n        data[field.name] = field_data\n\n    if extra_attrs:\n        for attr_name in extra_attrs:\n            attr = getattr(model, attr_name)\n            if callable_(attr):\n                data[attr_name] = attr()\n            else:\n                data[attr_name] = attr\n\n    if backrefs and recurse:\n        for foreign_key, rel_model in model._meta.backrefs.items():\n            if foreign_key.backref == '+': continue\n            descriptor = getattr(model_class, foreign_key.backref)\n            if descriptor in exclude or foreign_key in exclude:\n                continue\n            if only and (descriptor not in only) and (foreign_key not in only):\n                continue\n\n            accum = []\n            exclude.add(foreign_key)\n            related_query = getattr(model, foreign_key.backref)\n\n            for rel_obj in related_query:\n                accum.append(model_to_dict(\n                    rel_obj,\n                    recurse=recurse,\n                    backrefs=backrefs,\n                    only=only,\n                    exclude=exclude,\n                    max_depth=max_depth - 1))\n\n            data[foreign_key.backref] = accum\n\n    return data\n\n\ndef update_model_from_dict(instance, data, ignore_unknown=False):\n    meta = instance._meta\n    backrefs = dict([(fk.backref, fk) for fk in meta.backrefs])\n\n    for key, value in data.items():\n        if key in meta.combined:\n            field = meta.combined[key]\n            is_backref = False\n        elif key in backrefs:\n            field = backrefs[key]\n            is_backref = True\n        elif ignore_unknown:\n            setattr(instance, key, value)\n            continue\n        else:\n            raise AttributeError('Unrecognized attribute \"%s\" for model '\n                                 'class %s.' % (key, type(instance)))\n\n        is_foreign_key = isinstance(field, ForeignKeyField)\n\n        if not is_backref and is_foreign_key and isinstance(value, dict):\n            try:\n                rel_instance = instance.__rel__[field.name]\n            except KeyError:\n                rel_instance = field.rel_model()\n            setattr(\n                instance,\n                field.name,\n                update_model_from_dict(rel_instance, value, ignore_unknown))\n        elif is_backref and isinstance(value, (list, tuple)):\n            instances = [\n                dict_to_model(field.model, row_data, ignore_unknown)\n                for row_data in value]\n            for rel_instance in instances:\n                setattr(rel_instance, field.name, instance)\n            setattr(instance, field.backref, instances)\n        else:\n            setattr(instance, field.name, value)\n\n    return instance\n\n\ndef dict_to_model(model_class, data, ignore_unknown=False):\n    return update_model_from_dict(model_class(), data, ignore_unknown)\n\n\ndef insert_where(cls, data, where=None):\n    \"\"\"\n    Helper for generating conditional INSERT queries.\n\n    For example, prevent INSERTing a new tweet if the user has tweeted within\n    the last hour::\n\n        INSERT INTO \"tweet\" (\"user_id\", \"content\", \"timestamp\")\n        SELECT 234, 'some content', now()\n        WHERE NOT EXISTS (\n            SELECT 1 FROM \"tweet\"\n            WHERE user_id = 234 AND timestamp > now() - interval '1 hour')\n\n    Using this helper:\n\n        cond = ~fn.EXISTS(Tweet.select().where(\n            Tweet.user == user_obj,\n            Tweet.timestamp > one_hour_ago))\n\n        iq = insert_where(Tweet, {\n            Tweet.user: user_obj,\n            Tweet.content: 'some content'}, where=cond)\n\n        res = iq.execute()\n    \"\"\"\n    for field, default in cls._meta.defaults.items():\n        if field.name in data or field in data: continue\n        value = default() if callable_(default) else default\n        data[field] = value\n    fields, values = zip(*data.items())\n    sq = Select(columns=values).where(where)\n    return cls.insert_from(sq, fields).as_rowcount()\n\n\nclass ReconnectMixin(object):\n    \"\"\"\n    Mixin class that attempts to automatically reconnect to the database under\n    certain error conditions.\n\n    For example, MySQL servers will typically close connections that are idle\n    for 28800 seconds (\"wait_timeout\" setting). If your application makes use\n    of long-lived connections, you may find your connections are closed after\n    a period of no activity. This mixin will attempt to reconnect automatically\n    when these errors occur.\n\n    This mixin class probably should not be used with Postgres (unless you\n    REALLY know what you are doing) and definitely has no business being used\n    with Sqlite. If you wish to use with Postgres, you will need to adapt the\n    `reconnect_errors` attribute to something appropriate for Postgres.\n    \"\"\"\n    reconnect_errors = (\n        # Error class, error message fragment (or empty string for all).\n        (OperationalError, '2006'),  # MySQL server has gone away.\n        (OperationalError, '2013'),  # Lost connection to MySQL server.\n        (OperationalError, '2014'),  # Commands out of sync.\n        (OperationalError, '4031'),  # Client interaction timeout.\n\n        # mysql-connector raises a slightly different error when an idle\n        # connection is terminated by the server. This is equivalent to 2013.\n        (OperationalError, 'MySQL Connection not available.'),\n\n        # Postgres error examples:\n        #(OperationalError, 'terminat'),\n        #(InterfaceError, 'connection already closed'),\n    )\n\n    def __init__(self, *args, **kwargs):\n        super(ReconnectMixin, self).__init__(*args, **kwargs)\n\n        # Normalize the reconnect errors to a more efficient data-structure.\n        self._reconnect_errors = {}\n        for exc_class, err_fragment in self.reconnect_errors:\n            self._reconnect_errors.setdefault(exc_class, [])\n            self._reconnect_errors[exc_class].append(err_fragment.lower())\n\n    def execute_sql(self, sql, params=None):\n        return self._reconnect(super(ReconnectMixin, self).execute_sql,\n                               sql, params)\n\n    def begin(self, *args, **kwargs):\n        return self._reconnect(super(ReconnectMixin, self).begin,\n                               *args, **kwargs)\n\n    def _reconnect(self, func, *args, **kwargs):\n        try:\n            return func(*args, **kwargs)\n        except Exception as exc:\n            # If we are in a transaction, do not reconnect silently as\n            # any changes could be lost.\n            if self.in_transaction():\n                raise exc\n\n            exc_class = type(exc)\n            if exc_class not in self._reconnect_errors:\n                raise exc\n\n            exc_repr = str(exc).lower()\n            for err_fragment in self._reconnect_errors[exc_class]:\n                if err_fragment in exc_repr:\n                    break\n            else:\n                raise exc\n\n            if not self.is_closed():\n                self.close()\n                self.connect()\n\n            return func(*args, **kwargs)\n\n\ndef resolve_multimodel_query(query, key='_model_identifier'):\n    mapping = {}\n    accum = [query]\n    while accum:\n        curr = accum.pop()\n        if isinstance(curr, CompoundSelectQuery):\n            accum.extend((curr.lhs, curr.rhs))\n            continue\n\n        model_class = curr.model\n        name = model_class._meta.table_name\n        mapping[name] = model_class\n        curr._returning.append(Value(name).alias(key))\n\n    def wrapped_iterator():\n        for row in query.dicts().iterator():\n            identifier = row.pop(key)\n            model = mapping[identifier]\n            yield model(**row)\n\n    return wrapped_iterator()\n\n\nclass ThreadSafeDatabaseMetadata(Metadata):\n    \"\"\"\n    Metadata class to allow swapping database at run-time in a multi-threaded\n    application. To use:\n\n    class Base(Model):\n        class Meta:\n            model_metadata_class = ThreadSafeDatabaseMetadata\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        # The database attribute is stored in a thread-local.\n        self._database = None\n        self._table = None\n        self._lock = threading.Lock()\n        self._local = threading.local()\n        super(ThreadSafeDatabaseMetadata, self).__init__(*args, **kwargs)\n\n    def _get_db(self):\n        return getattr(self._local, 'database', self._database)\n    def _set_db(self, db):\n        if self._database is None:\n            self._database = db\n        self._local.database = db\n    database = property(_get_db, _set_db)\n\n    def set_database(self, database):\n        with self._lock:\n            return super(ThreadSafeDatabaseMetadata,\n                         self).set_database(database)\n\n    @property\n    def table(self):\n        if getattr(self._local, 'table', None) is None:\n            self._local.table = super(ThreadSafeDatabaseMetadata, self).table\n        return self._local.table\n    @table.setter\n    def table(self, value):\n        raise AttributeError('Cannot set the \"table\".')\n    @table.deleter\n    def table(self):\n        self._local.table = None\n"
  },
  {
    "path": "playhouse/signals.py",
    "content": "\"\"\"\nProvide django-style hooks for model events.\n\"\"\"\nfrom peewee import Model as _Model\n\n\nclass Signal(object):\n    def __init__(self):\n        self._flush()\n\n    def _flush(self):\n        self._receivers = set()\n        self._receiver_list = []\n\n    def connect(self, receiver, name=None, sender=None):\n        name = name or receiver.__name__\n        key = (name, sender)\n        if key not in self._receivers:\n            self._receivers.add(key)\n            self._receiver_list.append((name, receiver, sender))\n        else:\n            raise ValueError('receiver named %s (for sender=%s) already '\n                             'connected' % (name, sender or 'any'))\n\n    def disconnect(self, receiver=None, name=None, sender=None):\n        if receiver:\n            name = name or receiver.__name__\n        if not name:\n            raise ValueError('a receiver or a name must be provided')\n\n        key = (name, sender)\n        if key not in self._receivers:\n            raise ValueError('receiver named %s for sender=%s not found.' %\n                             (name, sender or 'any'))\n\n        self._receivers.remove(key)\n        self._receiver_list = [(n, r, s) for n, r, s in self._receiver_list\n                               if (n, s) != key]\n\n    def __call__(self, name=None, sender=None):\n        def decorator(fn):\n            self.connect(fn, name, sender)\n            return fn\n        return decorator\n\n    def send(self, instance, *args, **kwargs):\n        sender = type(instance)\n        responses = []\n        for n, r, s in self._receiver_list:\n            if s is None or isinstance(instance, s):\n                responses.append((r, r(sender, instance, *args, **kwargs)))\n        return responses\n\n\npre_save = Signal()\npost_save = Signal()\npre_delete = Signal()\npost_delete = Signal()\npre_init = Signal()\n\n\nclass Model(_Model):\n    def __init__(self, *args, **kwargs):\n        super(Model, self).__init__(*args, **kwargs)\n        pre_init.send(self)\n\n    def save(self, *args, **kwargs):\n        pk_value = self._pk if self._meta.primary_key else True\n        created = kwargs.get('force_insert', False) or not bool(pk_value)\n        pre_save.send(self, created=created)\n        ret = super(Model, self).save(*args, **kwargs)\n        post_save.send(self, created=created)\n        return ret\n\n    def delete_instance(self, *args, **kwargs):\n        pre_delete.send(self)\n        ret = super(Model, self).delete_instance(*args, **kwargs)\n        post_delete.send(self)\n        return ret\n"
  },
  {
    "path": "playhouse/sqlcipher_ext.py",
    "content": "\"\"\"\nPeewee integration with pysqlcipher.\n\nProject page: https://github.com/leapcode/pysqlcipher/\n\n**WARNING!!! EXPERIMENTAL!!!**\n\n* Although this extention's code is short, it has not been properly\n  peer-reviewed yet and may have introduced vulnerabilities.\n\nAlso note that this code relies on pysqlcipher and sqlcipher, and\nthe code there might have vulnerabilities as well, but since these\nare widely used crypto modules, we can expect \"short zero days\" there.\n\nExample usage:\n\n     from peewee.playground.ciphersql_ext import SqlCipherDatabase\n     db = SqlCipherDatabase('/path/to/my.db', passphrase=\"don'tuseme4real\")\n\n* `passphrase`: should be \"long enough\".\n  Note that *length beats vocabulary* (much exponential), and even\n  a lowercase-only passphrase like easytorememberyethardforotherstoguess\n  packs more noise than 8 random printable characters and *can* be memorized.\n\nWhen opening an existing database, passphrase should be the one used when the\ndatabase was created. If the passphrase is incorrect, an exception will only be\nraised **when you access the database**.\n\nIf you need to ask for an interactive passphrase, here's example code you can\nput after the `db = ...` line:\n\n    try:  # Just access the database so that it checks the encryption.\n        db.get_tables()\n    # We're looking for a DatabaseError with a specific error message.\n    except peewee.DatabaseError as e:\n        # Check whether the message *means* \"passphrase is wrong\"\n        if e.args[0] == 'file is encrypted or is not a database':\n            raise Exception('Developer should Prompt user for passphrase '\n                            'again.')\n        else:\n            # A different DatabaseError. Raise it.\n            raise e\n\nSee a more elaborate example with this code at\nhttps://gist.github.com/thedod/11048875\n\"\"\"\nimport datetime\nimport decimal\nimport sys\n\nfrom peewee import *\ntry:\n    from sqlcipher3 import dbapi2 as sqlcipher\nexcept ImportError:\n    from pysqlcipher3 import dbapi2 as sqlcipher\n\nsqlcipher.register_adapter(decimal.Decimal, str)\nsqlcipher.register_adapter(datetime.date, str)\nsqlcipher.register_adapter(datetime.time, str)\n__sqlcipher_version__ = sqlcipher.sqlite_version_info\n\n\nclass _SqlCipherDatabase(object):\n    server_version = __sqlcipher_version__\n\n    def _connect(self):\n        params = dict(self.connect_params)\n        passphrase = params.pop('passphrase', '').replace(\"'\", \"''\")\n\n        conn = sqlcipher.connect(self.database, isolation_level=None, **params)\n        try:\n            if passphrase:\n                conn.execute(\"PRAGMA key='%s'\" % passphrase)\n            self._add_conn_hooks(conn)\n        except:\n            conn.close()\n            raise\n        return conn\n\n    def set_passphrase(self, passphrase):\n        if not self.is_closed():\n            raise ImproperlyConfigured('Cannot set passphrase when database '\n                                       'is open. To change passphrase of an '\n                                       'open database use the rekey() method.')\n\n        self.connect_params['passphrase'] = passphrase\n\n    def rekey(self, passphrase):\n        if self.is_closed():\n            self.connect()\n\n        self.execute_sql(\"PRAGMA rekey='%s'\" % passphrase.replace(\"'\", \"''\"))\n        self.connect_params['passphrase'] = passphrase\n        return True\n\n\nclass SqlCipherDatabase(_SqlCipherDatabase, SqliteDatabase):\n    pass\n"
  },
  {
    "path": "playhouse/sqlite_changelog.py",
    "content": "from peewee import *\nfrom playhouse.sqlite_ext import JSONField\n\n\nclass BaseChangeLog(Model):\n    timestamp = DateTimeField(constraints=[SQL('DEFAULT CURRENT_TIMESTAMP')])\n    action = TextField()\n    table = TextField()\n    primary_key = IntegerField()\n    changes = JSONField()\n\n\nclass ChangeLog(object):\n    # Model class that will serve as the base for the changelog. This model\n    # will be subclassed and mapped to your application database.\n    base_model = BaseChangeLog\n\n    # Template for the triggers that handle updating the changelog table.\n    # table: table name\n    # action: insert / update / delete\n    # new_old: NEW or OLD (OLD is for DELETE)\n    # primary_key: table primary key column name\n    # column_array: output of build_column_array()\n    # change_table: changelog table name\n    template = \"\"\"CREATE TRIGGER IF NOT EXISTS %(table)s_changes_%(action)s\n    AFTER %(action)s ON %(table)s\n    BEGIN\n        INSERT INTO %(change_table)s\n            (\"action\", \"table\", \"primary_key\", \"changes\")\n        SELECT\n            '%(action)s', '%(table)s', %(new_old)s.\"%(primary_key)s\", \"changes\"\n        FROM (\n            SELECT json_group_object(\n                col,\n                json_array(\n                    case when json_valid(\"oldval\") then json(\"oldval\")\n                        else \"oldval\" end,\n                    case when json_valid(\"newval\") then json(\"newval\")\n                        else \"newval\" end)\n                ) AS \"changes\"\n            FROM (\n                SELECT json_extract(value, '$[0]') as \"col\",\n                       json_extract(value, '$[1]') as \"oldval\",\n                       json_extract(value, '$[2]') as \"newval\"\n                FROM json_each(json_array(%(column_array)s))\n                WHERE \"oldval\" IS NOT \"newval\"\n            )\n        );\n    END;\"\"\"\n\n    drop_template = 'DROP TRIGGER IF EXISTS %(table)s_changes_%(action)s'\n\n    _actions = ('INSERT', 'UPDATE', 'DELETE')\n\n    def __init__(self, db, table_name='changelog'):\n        self.db = db\n        self.table_name = table_name\n\n    def _build_column_array(self, model, use_old, use_new, skip_fields=None):\n        # Builds a list of SQL expressions for each field we are tracking. This\n        # is used as the data source for change tracking in our trigger.\n        col_array = []\n        for field in model._meta.sorted_fields:\n            if field.primary_key:\n                continue\n\n            if skip_fields is not None and field.name in skip_fields:\n                continue\n\n            column = field.column_name\n            new = 'NULL' if not use_new else 'NEW.\"%s\"' % column\n            old = 'NULL' if not use_old else 'OLD.\"%s\"' % column\n\n            if isinstance(field, JSONField):\n                # Ensure that values are cast to JSON so that the serialization\n                # is preserved when calculating the old / new.\n                if use_old: old = 'json(%s)' % old\n                if use_new: new = 'json(%s)' % new\n\n            col_array.append(\"json_array('%s', %s, %s)\" % (column, old, new))\n\n        return ', '.join(col_array)\n\n    def trigger_sql(self, model, action, skip_fields=None):\n        assert action in self._actions\n        use_old = action != 'INSERT'\n        use_new = action != 'DELETE'\n        cols = self._build_column_array(model, use_old, use_new, skip_fields)\n        return self.template % {\n            'table': model._meta.table_name,\n            'action': action,\n            'new_old': 'NEW' if action != 'DELETE' else 'OLD',\n            'primary_key': model._meta.primary_key.column_name,\n            'column_array': cols,\n            'change_table': self.table_name}\n\n    def drop_trigger_sql(self, model, action):\n        assert action in self._actions\n        return self.drop_template % {\n            'table': model._meta.table_name,\n            'action': action}\n\n    @property\n    def model(self):\n        if not hasattr(self, '_changelog_model'):\n            class ChangeLog(self.base_model):\n                class Meta:\n                    database = self.db\n                    table_name = self.table_name\n            self._changelog_model = ChangeLog\n\n        return self._changelog_model\n\n    def install(self, model, skip_fields=None, drop=True, insert=True,\n                update=True, delete=True, create_table=True):\n        ChangeLog = self.model\n        if create_table:\n            ChangeLog.create_table()\n\n        actions = list(zip((insert, update, delete), self._actions))\n        if drop:\n            for _, action in actions:\n                self.db.execute_sql(self.drop_trigger_sql(model, action))\n\n        for enabled, action in actions:\n            if enabled:\n                sql = self.trigger_sql(model, action, skip_fields)\n                self.db.execute_sql(sql)\n"
  },
  {
    "path": "playhouse/sqlite_ext.py",
    "content": "import json\nimport re\nimport sys\nimport warnings\n\nfrom peewee import *\nfrom peewee import ColumnBase\nfrom peewee import EnclosedNodeList\nfrom peewee import Entity\nfrom peewee import Expression\nfrom peewee import Insert\nfrom peewee import Node\nfrom peewee import NodeList\nfrom peewee import OP\nfrom peewee import VirtualField\nfrom peewee import merge_dict\nfrom peewee import sqlite3\nfrom playhouse.sqlite_udf import JSON\nfrom playhouse.sqlite_udf import RANK\nfrom playhouse.sqlite_udf import register_udf_groups\n\n\n\nFTS3_MATCHINFO = 'pcx'\nFTS4_MATCHINFO = 'pcnalx'\nif sqlite3 is not None:\n    FTS_VERSION = 4 if sqlite3.sqlite_version_info[:3] >= (3, 7, 4) else 3\nelse:\n    FTS_VERSION = 3\n\nFTS5_MIN_SQLITE_VERSION = (3, 9, 0)\n\n\nclass RowIDField(AutoField):\n    auto_increment = True\n    column_name = name = required_name = 'rowid'\n\n    def bind(self, model, name, *args):\n        if name != self.required_name:\n            raise ValueError('%s must be named \"%s\".' %\n                             (type(self), self.required_name))\n        super(RowIDField, self).bind(model, name, *args)\n\n\nclass DocIDField(RowIDField):\n    column_name = name = required_name = 'docid'\n\n\nclass AutoIncrementField(AutoField):\n    def ddl(self, ctx):\n        node_list = super(AutoIncrementField, self).ddl(ctx)\n        return NodeList((node_list, SQL('AUTOINCREMENT')))\n\n\nclass TDecimalField(DecimalField):\n    field_type = 'TEXT'\n    def get_modifiers(self): pass\n\n\nclass ISODateTimeField(DateTimeField):\n    formats = [\n        '%Y-%m-%dT%H:%M:%S.%f%z',\n        '%Y-%m-%dT%H:%M:%S%z',\n        '%Y-%m-%dT%H:%M:%S.%f',\n        '%Y-%m-%dT%H:%M:%S',\n        '%Y-%m-%d',\n    ]\n\n    def db_value(self, value):\n        if value:\n            return value.isoformat()\n\n\nclass JSONPath(ColumnBase):\n    def __init__(self, field, path=None):\n        super(JSONPath, self).__init__()\n        self._field = field\n        self._path = path or ()\n\n    def _converter(self, value):\n        return self._field.python_value(value)\n\n    @property\n    def path(self):\n        return Value('$%s' % ''.join(self._path))\n\n    def __getitem__(self, idx):\n        if isinstance(idx, int) or idx == '#':\n            item = '[%s]' % idx\n        else:\n            item = '.%s' % idx\n        return type(self)(self._field, self._path + (item,))\n\n    def append(self, value, as_json=None):\n        if as_json or isinstance(value, (list, dict)):\n            value = fn.json(self._field._json_dumps(value))\n        return fn.json_set(self._field, self['#'].path, value)\n\n    def _json_operation(self, func, value, as_json=None):\n        if as_json or isinstance(value, (list, dict)):\n            value = fn.json(self._field._json_dumps(value))\n        return func(self._field, self.path, value)\n\n    def insert(self, value, as_json=None):\n        return self._json_operation(fn.json_insert, value, as_json)\n\n    def set(self, value, as_json=None):\n        return self._json_operation(fn.json_set, value, as_json)\n\n    def replace(self, value, as_json=None):\n        return self._json_operation(fn.json_replace, value, as_json)\n\n    def update(self, value):\n        return self.set(fn.json_patch(self, self._field._json_dumps(value)))\n\n    def remove(self):\n        return fn.json_remove(self._field, self.path)\n\n    def json_type(self):\n        return fn.json_type(self._field, self.path)\n\n    def length(self):\n        return fn.json_array_length(self._field, self.path)\n\n    def children(self):\n        return fn.json_each(self._field, self.path)\n\n    def tree(self):\n        return fn.json_tree(self._field, self.path)\n\n    def __sql__(self, ctx):\n        return ctx.sql(fn.json_extract(self._field, self.path)\n                       if self._path else self._field)\n\nclass JSONBPath(JSONPath):\n    def append(self, value, as_json=None):\n        if as_json or isinstance(value, (list, dict)):\n            value = fn.jsonb(self._field._json_dumps(value))\n        return fn.jsonb_set(self._field, self['#'].path, value)\n\n    def _json_operation(self, func, value, as_json=None):\n        if as_json or isinstance(value, (list, dict)):\n            value = fn.jsonb(self._field._json_dumps(value))\n        return func(self._field, self.path, value)\n\n    def insert(self, value, as_json=None):\n        return self._json_operation(fn.jsonb_insert, value, as_json)\n\n    def set(self, value, as_json=None):\n        return self._json_operation(fn.jsonb_set, value, as_json)\n\n    def replace(self, value, as_json=None):\n        return self._json_operation(fn.jsonb_replace, value, as_json)\n\n    def update(self, value):\n        return self.set(fn.jsonb_patch(self, self._field._json_dumps(value)))\n\n    def remove(self):\n        return fn.jsonb_remove(self._field, self.path)\n\n    def __sql__(self, ctx):\n        return ctx.sql(fn.jsonb_extract(self._field, self.path)\n                       if self._path else self._field)\n\n\nclass JSONField(TextField):\n    field_type = 'JSON'\n    unpack = False\n    Path = JSONPath\n\n    def __init__(self, json_dumps=None, json_loads=None, **kwargs):\n        self._json_dumps = json_dumps or json.dumps\n        self._json_loads = json_loads or json.loads\n        super(JSONField, self).__init__(**kwargs)\n\n    def python_value(self, value):\n        if value is not None:\n            try:\n                return self._json_loads(value)\n            except (TypeError, ValueError):\n                return value\n\n    def db_value(self, value):\n        if value is not None:\n            if not isinstance(value, Node):\n                value = fn.json(self._json_dumps(value))\n            return value\n\n    def _e(op):\n        def inner(self, rhs):\n            if isinstance(rhs, (list, dict)):\n                rhs = AsIs(rhs, self.db_value)\n            return Expression(self, op, rhs)\n        return inner\n    __eq__ = _e(OP.EQ)\n    __ne__ = _e(OP.NE)\n    __gt__ = _e(OP.GT)\n    __ge__ = _e(OP.GTE)\n    __lt__ = _e(OP.LT)\n    __le__ = _e(OP.LTE)\n    __hash__ = Field.__hash__\n\n    def __getitem__(self, item):\n        return self.Path(self)[item]\n\n    def extract(self, *paths):\n        paths = [Value(p, converter=False) for p in paths]\n        return fn.json_extract(self, *paths)\n    def extract_json(self, path):\n        return Expression(self, '->', Value(path, converter=False))\n    def extract_text(self, path):\n        return Expression(self, '->>', Value(path, converter=False))\n\n    def append(self, value, as_json=None):\n        return self.Path(self).append(value, as_json)\n\n    def insert(self, value, as_json=None):\n        return self.Path(self).insert(value, as_json)\n\n    def set(self, value, as_json=None):\n        return self.Path(self).set(value, as_json)\n\n    def replace(self, value, as_json=None):\n        return self.Path(self).replace(value, as_json)\n\n    def update(self, data):\n        return self.Path(self).update(data)\n\n    def remove(self, *paths):\n        if not paths:\n            return self.Path(self).remove()\n        return fn.json_remove(self, *paths)\n\n    def json_type(self):\n        return fn.json_type(self)\n\n    def length(self, path=None):\n        args = (self, path) if path else (self,)\n        return fn.json_array_length(*args)\n\n    def children(self):\n        \"\"\"\n        Schema of `json_each` and `json_tree`:\n\n        key,\n        value,\n        type TEXT (object, array, string, etc),\n        atom (value for primitive/scalar types, NULL for array and object)\n        id INTEGER (unique identifier for element)\n        parent INTEGER (unique identifier of parent element or NULL)\n        fullkey TEXT (full path describing element)\n        path TEXT (path to the container of the current element)\n        json JSON hidden (1st input parameter to function)\n        root TEXT hidden (2nd input parameter, path at which to start)\n        \"\"\"\n        return fn.json_each(self)\n\n    def tree(self):\n        return fn.json_tree(self)\n\n\nclass JSONBField(JSONField):\n    field_type = 'JSONB'\n    Path = JSONBPath\n\n    def db_value(self, value):\n        if value is not None:\n            if not isinstance(value, Node):\n                value = fn.jsonb(self._json_dumps(value))\n            return value\n\n    def json(self):\n        return fn.json(self)\n\n    def extract(self, *paths):\n        paths = [Value(p, converter=False) for p in paths]\n        return fn.jsonb_extract(self, *paths)\n\n    def remove(self, *paths):\n        if not paths:\n            return self.Path(self).remove()\n        return fn.jsonb_remove(self, *paths)\n\n\nclass SearchField(Field):\n    def __init__(self, unindexed=False, column_name=None, **k):\n        if k:\n            raise ValueError('SearchField does not accept these keyword '\n                             'arguments: %s.' % sorted(k))\n        super(SearchField, self).__init__(unindexed=unindexed,\n                                          column_name=column_name, null=True)\n\n    def match(self, term):\n        return match(self, term)\n\n    @property\n    def fts_column_index(self):\n        if not hasattr(self, '_fts_column_index'):\n            search_fields = [f.name for f in self.model._meta.sorted_fields\n                             if isinstance(f, SearchField)]\n            self._fts_column_index = search_fields.index(self.name)\n        return self._fts_column_index\n\n    def highlight(self, left, right):\n        column_idx = self.fts_column_index\n        return fn.highlight(self.model._meta.entity, column_idx, left, right)\n\n    def snippet(self, left, right, over_length='...', max_tokens=16):\n        if not (0 < max_tokens < 65):\n            raise ValueError('max_tokens must be between 1 and 64 (inclusive)')\n        column_idx = self.fts_column_index\n        return fn.snippet(self.model._meta.entity, column_idx, left, right,\n                          over_length, max_tokens)\n\n\nclass VirtualTableSchemaManager(SchemaManager):\n    def _create_virtual_table(self, safe=True, **options):\n        options = self.model.clean_options(\n            merge_dict(self.model._meta.options, options))\n\n        # Structure:\n        # CREATE VIRTUAL TABLE <model>\n        # USING <extension_module>\n        # ([prefix_arguments, ...] fields, ... [arguments, ...], [options...])\n        ctx = self._create_context()\n        ctx.literal('CREATE VIRTUAL TABLE ')\n        if safe:\n            ctx.literal('IF NOT EXISTS ')\n        (ctx\n         .sql(self.model)\n         .literal(' USING '))\n\n        ext_module = self.model._meta.extension_module\n        if isinstance(ext_module, Node):\n            return ctx.sql(ext_module)\n\n        ctx.sql(SQL(ext_module)).literal(' ')\n        arguments = []\n        meta = self.model._meta\n\n        if meta.prefix_arguments:\n            arguments.extend([SQL(a) for a in meta.prefix_arguments])\n\n        # Constraints, data-types, foreign and primary keys are all omitted.\n        for field in meta.sorted_fields:\n            if isinstance(field, (RowIDField)) or field._hidden:\n                continue\n            field_def = [Entity(field.column_name)]\n            if field.unindexed:\n                field_def.append(SQL('UNINDEXED'))\n            arguments.append(NodeList(field_def))\n\n        if meta.arguments:\n            arguments.extend([SQL(a) for a in meta.arguments])\n\n        if options:\n            arguments.extend(self._create_table_option_sql(options))\n        return ctx.sql(EnclosedNodeList(arguments))\n\n    def _create_table(self, safe=True, **options):\n        if issubclass(self.model, VirtualModel):\n            return self._create_virtual_table(safe, **options)\n\n        return super(VirtualTableSchemaManager, self)._create_table(\n            safe, **options)\n\n\nclass VirtualModel(Model):\n    class Meta:\n        arguments = None\n        extension_module = None\n        prefix_arguments = None\n        primary_key = False\n        schema_manager_class = VirtualTableSchemaManager\n\n    @classmethod\n    def clean_options(cls, options):\n        return options\n\n\nclass BaseFTSModel(VirtualModel):\n    @classmethod\n    def clean_options(cls, options):\n        content = options.get('content')\n        prefix = options.get('prefix')\n        tokenize = options.get('tokenize')\n        content_rowid = options.get('content_rowid')\n\n        if isinstance(content, str) and content == '':\n            # Special-case content-less full-text search tables.\n            options['content'] = \"''\"\n        elif isinstance(content, Field):\n            # Special-case to ensure fields are fully-qualified.\n            options['content'] = Entity(content.model._meta.table_name,\n                                        content.column_name)\n\n        if content_rowid is not None:\n            options['content_rowid'] = content_rowid\n\n        if prefix:\n            if isinstance(prefix, (list, tuple)):\n                prefix = ','.join([str(i) for i in prefix])\n            options['prefix'] = \"'%s'\" % prefix.strip(\"' \")\n\n        if tokenize and cls._meta.extension_module.lower() == 'fts5':\n            # Tokenizers need to be in quoted string for FTS5, but not for FTS3\n            # or FTS4.\n            options['tokenize'] = '\"%s\"' % tokenize\n\n        return options\n\n\nclass FTSModel(BaseFTSModel):\n    \"\"\"\n    VirtualModel class for creating tables that use either the FTS3 or FTS4\n    search extensions. Peewee automatically determines which version of the\n    FTS extension is supported and will use FTS4 if possible.\n    \"\"\"\n    # FTS3/4 uses \"docid\" in the same way a normal table uses \"rowid\".\n    docid = DocIDField()\n\n    class Meta:\n        extension_module = 'FTS%s' % FTS_VERSION\n\n    @classmethod\n    def _fts_cmd(cls, cmd):\n        tbl = cls._meta.table_name\n        res = cls._meta.database.execute_sql(\n            \"INSERT INTO %s(%s) VALUES('%s');\" % (tbl, tbl, cmd))\n        return res.fetchone()\n\n    @classmethod\n    def optimize(cls):\n        return cls._fts_cmd('optimize')\n\n    @classmethod\n    def rebuild(cls):\n        return cls._fts_cmd('rebuild')\n\n    @classmethod\n    def integrity_check(cls):\n        return cls._fts_cmd('integrity-check')\n\n    @classmethod\n    def merge(cls, blocks=200, segments=8):\n        return cls._fts_cmd('merge=%s,%s' % (blocks, segments))\n\n    @classmethod\n    def automerge(cls, state=True):\n        return cls._fts_cmd('automerge=%s' % (state and '1' or '0'))\n\n    @classmethod\n    def match(cls, term):\n        \"\"\"\n        Generate a `MATCH` expression appropriate for searching this table.\n        \"\"\"\n        return match(cls._meta.entity, term)\n\n    @classmethod\n    def rank(cls, *weights):\n        matchinfo = fn.matchinfo(cls._meta.entity, FTS3_MATCHINFO)\n        return fn.fts_rank(matchinfo, *weights)\n\n    @classmethod\n    def bm25(cls, *weights):\n        match_info = fn.matchinfo(cls._meta.entity, FTS4_MATCHINFO)\n        return fn.fts_bm25(match_info, *weights)\n\n    @classmethod\n    def bm25f(cls, *weights):\n        match_info = fn.matchinfo(cls._meta.entity, FTS4_MATCHINFO)\n        return fn.fts_bm25f(match_info, *weights)\n\n    @classmethod\n    def lucene(cls, *weights):\n        match_info = fn.matchinfo(cls._meta.entity, FTS4_MATCHINFO)\n        return fn.fts_lucene(match_info, *weights)\n\n    @classmethod\n    def _search(cls, term, weights, with_score, score_alias, score_fn,\n                explicit_ordering):\n        if not weights:\n            rank = score_fn()\n        elif isinstance(weights, dict):\n            weight_args = []\n            for field in cls._meta.sorted_fields:\n                # Attempt to get the specified weight of the field by looking\n                # it up using it's field instance followed by name.\n                field_weight = weights.get(field, weights.get(field.name, 1.0))\n                weight_args.append(field_weight)\n            rank = score_fn(*weight_args)\n        else:\n            rank = score_fn(*weights)\n\n        selection = ()\n        order_by = rank\n        if with_score:\n            selection = (cls, rank.alias(score_alias))\n        if with_score and not explicit_ordering:\n            order_by = SQL(score_alias)\n\n        return (cls\n                .select(*selection)\n                .where(cls.match(term))\n                .order_by(order_by))\n\n    @classmethod\n    def search(cls, term, weights=None, with_score=False, score_alias='score',\n               explicit_ordering=False):\n        \"\"\"Full-text search using selected `term`.\"\"\"\n        return cls._search(\n            term,\n            weights,\n            with_score,\n            score_alias,\n            cls.rank,\n            explicit_ordering)\n\n    @classmethod\n    def search_bm25(cls, term, weights=None, with_score=False,\n                    score_alias='score', explicit_ordering=False):\n        \"\"\"Full-text search for selected `term` using BM25 algorithm.\"\"\"\n        return cls._search(\n            term,\n            weights,\n            with_score,\n            score_alias,\n            cls.bm25,\n            explicit_ordering)\n\n    @classmethod\n    def search_bm25f(cls, term, weights=None, with_score=False,\n                     score_alias='score', explicit_ordering=False):\n        \"\"\"Full-text search for selected `term` using BM25 algorithm.\"\"\"\n        return cls._search(\n            term,\n            weights,\n            with_score,\n            score_alias,\n            cls.bm25f,\n            explicit_ordering)\n\n    @classmethod\n    def search_lucene(cls, term, weights=None, with_score=False,\n                      score_alias='score', explicit_ordering=False):\n        \"\"\"Full-text search for selected `term` using BM25 algorithm.\"\"\"\n        return cls._search(\n            term,\n            weights,\n            with_score,\n            score_alias,\n            cls.lucene,\n            explicit_ordering)\n\n\n_alphabet = 'abcdefghijklmnopqrstuvwxyz'\n_alphanum = (set('\\t ,\"(){}*:_+0123456789') |\n             set(_alphabet) |\n             set(_alphabet.upper()) |\n             set((chr(26),)))\n_invalid_ascii = set(chr(p) for p in range(128) if chr(p) not in _alphanum)\ndel _alphabet\ndel _alphanum\n_quote_re = re.compile(r'[^\\s\"]+|\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"')\n\n\nclass FTS5Model(BaseFTSModel):\n    \"\"\"\n    Requires SQLite >= 3.9.0.\n\n    Table options:\n\n    content: table name of external content, or empty string for \"contentless\"\n    content_rowid: column name of external content primary key\n    prefix: integer(s). Ex: '2' or '2 3 4'\n    tokenize: porter, unicode61, ascii. Ex: 'porter unicode61'\n\n    The unicode tokenizer supports the following parameters:\n\n    * remove_diacritics (1 or 0, default is 1)\n    * tokenchars (string of characters, e.g. '-_'\n    * separators (string of characters)\n\n    Parameters are passed as alternating parameter name and value, so:\n\n    {'tokenize': \"unicode61 remove_diacritics 0 tokenchars '-_'\"}\n\n    Content-less tables:\n\n    If you don't need the full-text content in it's original form, you can\n    specify a content-less table. Searches and auxiliary functions will work\n    as usual, but the only values returned when SELECT-ing can be rowid. Also\n    content-less tables do not support UPDATE or DELETE.\n\n    External content tables:\n\n    You can set up triggers to sync these, e.g.\n\n    -- Create a table. And an external content fts5 table to index it.\n    CREATE TABLE tbl(a INTEGER PRIMARY KEY, b);\n    CREATE VIRTUAL TABLE ft USING fts5(b, content='tbl', content_rowid='a');\n\n    -- Triggers to keep the FTS index up to date.\n    CREATE TRIGGER tbl_ai AFTER INSERT ON tbl BEGIN\n      INSERT INTO ft(rowid, b) VALUES (new.a, new.b);\n    END;\n    CREATE TRIGGER tbl_ad AFTER DELETE ON tbl BEGIN\n      INSERT INTO ft(fts_idx, rowid, b) VALUES('delete', old.a, old.b);\n    END;\n    CREATE TRIGGER tbl_au AFTER UPDATE ON tbl BEGIN\n      INSERT INTO ft(fts_idx, rowid, b) VALUES('delete', old.a, old.b);\n      INSERT INTO ft(rowid, b) VALUES (new.a, new.b);\n    END;\n\n    Built-in auxiliary functions:\n\n    * bm25(tbl[, weight_0, ... weight_n])\n    * highlight(tbl, col_idx, prefix, suffix)\n    * snippet(tbl, col_idx, prefix, suffix, ?, max_tokens)\n    \"\"\"\n    # FTS5 does not support declared primary keys, but we can use the\n    # implicit rowid.\n    rowid = RowIDField()\n\n    class Meta:\n        extension_module = 'fts5'\n\n    _error_messages = {\n        'field_type': ('Besides the implicit `rowid` column, all columns must '\n                       'be instances of SearchField'),\n        'index': 'Secondary indexes are not supported for FTS5 models',\n        'pk': 'FTS5 models must use the default `rowid` primary key',\n    }\n\n    @classmethod\n    def validate_model(cls):\n        # Perform FTS5-specific validation and options post-processing.\n        if cls._meta.primary_key.name != 'rowid':\n            raise ImproperlyConfigured(cls._error_messages['pk'])\n        for field in cls._meta.fields.values():\n            if not isinstance(field, (SearchField, RowIDField)):\n                raise ImproperlyConfigured(cls._error_messages['field_type'])\n        if cls._meta.indexes:\n            raise ImproperlyConfigured(cls._error_messages['index'])\n\n    @classmethod\n    def fts5_installed(cls):\n        if sqlite3.sqlite_version_info[:3] < FTS5_MIN_SQLITE_VERSION:\n            return False\n\n        # Test in-memory DB to determine if the FTS5 extension is installed.\n        tmp_db = sqlite3.connect(':memory:')\n        try:\n            tmp_db.execute('CREATE VIRTUAL TABLE fts5test USING fts5 (data);')\n        except:\n            try:\n                tmp_db.enable_load_extension(True)\n                tmp_db.load_extension('fts5')\n            except:\n                return False\n            else:\n                cls._meta.database.load_extension('fts5')\n        finally:\n            tmp_db.close()\n\n        return True\n\n    @staticmethod\n    def validate_query(query):\n        \"\"\"\n        Simple helper function to indicate whether a search query is a\n        valid FTS5 query. Note: this simply looks at the characters being\n        used, and is not guaranteed to catch all problematic queries.\n        \"\"\"\n        tokens = _quote_re.findall(query)\n        for token in tokens:\n            if token.startswith('\"') and token.endswith('\"'):\n                continue\n            if set(token) & _invalid_ascii:\n                return False\n        return True\n\n    @staticmethod\n    def clean_query(query, replace=chr(26)):\n        \"\"\"\n        Clean a query of invalid tokens.\n        \"\"\"\n        accum = []\n        any_invalid = False\n        tokens = _quote_re.findall(query)\n        for token in tokens:\n            if token.startswith('\"') and token.endswith('\"'):\n                accum.append(token)\n                continue\n            token_set = set(token)\n            invalid_for_token = token_set & _invalid_ascii\n            if invalid_for_token:\n                any_invalid = True\n                for c in invalid_for_token:\n                    token = token.replace(c, replace)\n            accum.append(token)\n\n        if any_invalid:\n            return ' '.join(accum)\n        return query\n\n    @classmethod\n    def match(cls, term):\n        \"\"\"\n        Generate a `MATCH` expression appropriate for searching this table.\n        \"\"\"\n        return match(cls._meta.entity, term)\n\n    @classmethod\n    def rank(cls, *args):\n        return cls.bm25(*args) if args else SQL('rank')\n\n    @classmethod\n    def bm25(cls, *weights):\n        return fn.bm25(cls._meta.entity, *weights)\n\n    @classmethod\n    def search(cls, term, weights=None, with_score=False, score_alias='score',\n               explicit_ordering=False):\n        \"\"\"Full-text search using selected `term`.\"\"\"\n        return cls.search_bm25(\n            FTS5Model.clean_query(term),\n            weights,\n            with_score,\n            score_alias,\n            explicit_ordering)\n\n    @classmethod\n    def search_bm25(cls, term, weights=None, with_score=False,\n                    score_alias='score', explicit_ordering=False):\n        \"\"\"Full-text search using selected `term`.\"\"\"\n        if not weights:\n            rank = SQL('rank')\n        elif isinstance(weights, dict):\n            weight_args = []\n            for field in cls._meta.sorted_fields:\n                if isinstance(field, SearchField) and not field.unindexed:\n                    weight_args.append(\n                        weights.get(field, weights.get(field.name, 1.0)))\n            rank = fn.bm25(cls._meta.entity, *weight_args)\n        else:\n            rank = fn.bm25(cls._meta.entity, *weights)\n\n        selection = ()\n        order_by = rank\n        if with_score:\n            selection = (cls, rank.alias(score_alias))\n        if with_score and not explicit_ordering:\n            order_by = SQL(score_alias)\n\n        return (cls\n                .select(*selection)\n                .where(cls.match(FTS5Model.clean_query(term)))\n                .order_by(order_by))\n\n    @classmethod\n    def _fts_cmd_sql(cls, cmd, **extra_params):\n        tbl = cls._meta.entity\n        columns = [tbl]\n        values = [cmd]\n        for key, value in extra_params.items():\n            columns.append(Entity(key))\n            values.append(value)\n\n        return NodeList((\n            SQL('INSERT INTO'),\n            cls._meta.entity,\n            EnclosedNodeList(columns),\n            SQL('VALUES'),\n            EnclosedNodeList(values)))\n\n    @classmethod\n    def _fts_cmd(cls, cmd, **extra_params):\n        query = cls._fts_cmd_sql(cmd, **extra_params)\n        return cls._meta.database.execute(query)\n\n    @classmethod\n    def automerge(cls, level):\n        if not (0 <= level <= 16):\n            raise ValueError('level must be between 0 and 16')\n        return cls._fts_cmd('automerge', rank=level)\n\n    @classmethod\n    def merge(cls, npages):\n        return cls._fts_cmd('merge', rank=npages)\n\n    @classmethod\n    def optimize(cls):\n        return cls._fts_cmd('optimize')\n\n    @classmethod\n    def rebuild(cls):\n        return cls._fts_cmd('rebuild')\n\n    @classmethod\n    def set_pgsz(cls, pgsz):\n        return cls._fts_cmd('pgsz', rank=pgsz)\n\n    @classmethod\n    def set_rank(cls, rank_expression):\n        return cls._fts_cmd('rank', rank=rank_expression)\n\n    @classmethod\n    def delete_all(cls):\n        return cls._fts_cmd('delete-all')\n\n    @classmethod\n    def integrity_check(cls, rank=0):\n        return cls._fts_cmd('integrity-check', rank=rank)\n\n    @classmethod\n    def VocabModel(cls, table_type='row', table=None):\n        if table_type not in ('row', 'col', 'instance'):\n            raise ValueError('table_type must be either \"row\", \"col\" or '\n                             '\"instance\".')\n\n        attr = '_vocab_model_%s' % table_type\n\n        if not hasattr(cls, attr):\n            class Meta:\n                database = cls._meta.database\n                table_name = table or cls._meta.table_name + '_v'\n                extension_module = fn.fts5vocab(\n                    cls._meta.entity,\n                    SQL(table_type))\n\n            attrs = {\n                'term': VirtualField(TextField),\n                'doc': IntegerField(),\n                'cnt': IntegerField(),\n                'rowid': RowIDField(),\n                'Meta': Meta,\n            }\n            if table_type == 'col':\n                attrs['col'] = VirtualField(TextField)\n            elif table_type == 'instance':\n                attrs['offset'] = VirtualField(IntegerField)\n\n            class_name = '%sVocab' % cls.__name__\n            setattr(cls, attr, type(class_name, (VirtualModel,), attrs))\n\n        return getattr(cls, attr)\n\n\nOP.MATCH = 'MATCH'\n\ndef match(lhs, rhs):\n    return Expression(lhs, OP.MATCH, rhs)\n"
  },
  {
    "path": "playhouse/sqlite_udf.py",
    "content": "import collections\nimport datetime\nimport heapq\nimport json\nimport math\nimport os\nimport random\nimport re\nimport struct\nimport sys\nimport threading\nimport zlib\ntry:\n    from urllib.parse import urlparse\nexcept ImportError:\n    from urlparse import urlparse\n\n\nSQLITE_DATETIME_FORMATS = (\n    '%Y-%m-%d %H:%M:%S.%f',\n    '%Y-%m-%d %H:%M:%S.%f%z',\n    '%Y-%m-%d %H:%M:%S',\n    '%Y-%m-%d %H:%M:%S%z',\n    '%Y-%m-%d',\n    '%H:%M:%S',\n    '%H:%M:%S.%f',\n    '%H:%M')\n\nfrom peewee import format_date_time\n\ndef format_date_time_sqlite(date_value):\n    return format_date_time(date_value, SQLITE_DATETIME_FORMATS)\n\ntry:\n    from playhouse import _sqlite_udf as cython_udf\nexcept ImportError:\n    cython_udf = None\n\n\n# Group udf by function.\nCONTROL_FLOW = 'control_flow'\nDATE = 'date'\nFILE = 'file'\nHELPER = 'helpers'\nJSON = 'json'\nMATH = 'math'\nRANK = 'rank'\nSTRING = 'string'\n\nAGGREGATE_COLLECTION = {}\nUDF_COLLECTION = {}\n\n\nclass synchronized_dict(dict):\n    def __init__(self, *args, **kwargs):\n        super(synchronized_dict, self).__init__(*args, **kwargs)\n        self._lock = threading.Lock()\n\n    def __getitem__(self, key):\n        with self._lock:\n            return super(synchronized_dict, self).__getitem__(key)\n\n    def __setitem__(self, key, value):\n        with self._lock:\n            return super(synchronized_dict, self).__setitem__(key, value)\n\n    def __delitem__(self, key):\n        with self._lock:\n            return super(synchronized_dict, self).__delitem__(key)\n\n\nSTATE = synchronized_dict()\nSETTINGS = synchronized_dict()\n\n# Class and function decorators.\ndef aggregate(*groups):\n    def decorator(klass):\n        for group in groups:\n            AGGREGATE_COLLECTION.setdefault(group, [])\n            AGGREGATE_COLLECTION[group].append(klass)\n        return klass\n    return decorator\n\ndef udf(group, name=None):\n    def decorator(fn):\n        UDF_COLLECTION.setdefault(group, [])\n        UDF_COLLECTION[group].append((fn, name or fn.__name__))\n        return fn\n    return decorator\n\n# Register aggregates / functions with connection.\ndef register_aggregate_groups(db, *groups):\n    seen = set()\n    for group in groups:\n        klasses = AGGREGATE_COLLECTION.get(group, ())\n        for klass in klasses:\n            name = getattr(klass, 'name', klass.__name__)\n            if name not in seen:\n                seen.add(name)\n                db.register_aggregate(klass, name)\n\ndef register_udf_groups(db, *groups):\n    seen = set()\n    for group in groups:\n        functions = UDF_COLLECTION.get(group, ())\n        for function, name in functions:\n            if name not in seen:\n                seen.add(name)\n                db.register_function(function, name)\n\ndef register_groups(db, *groups):\n    register_aggregate_groups(db, *groups)\n    register_udf_groups(db, *groups)\n\ndef register_all(db):\n    register_aggregate_groups(db, *AGGREGATE_COLLECTION)\n    register_udf_groups(db, *UDF_COLLECTION)\n\n\n# Begin actual user-defined functions and aggregates.\n\n# Scalar functions.\n@udf(CONTROL_FLOW)\ndef if_then_else(cond, truthy, falsey=None):\n    if cond:\n        return truthy\n    return falsey\n\n@udf(DATE)\ndef strip_tz(date_str):\n    date_str = date_str.replace('T', ' ')\n    tz_idx1 = date_str.find('+')\n    if tz_idx1 != -1:\n        return date_str[:tz_idx1]\n    tz_idx2 = date_str.find('-')\n    if tz_idx2 > 13:\n        return date_str[:tz_idx2]\n    return date_str\n\n@udf(DATE)\ndef human_delta(nseconds, glue=', '):\n    parts = (\n        (86400 * 365, 'year'),\n        (86400 * 30, 'month'),\n        (86400 * 7, 'week'),\n        (86400, 'day'),\n        (3600, 'hour'),\n        (60, 'minute'),\n        (1, 'second'),\n    )\n    accum = []\n    for offset, name in parts:\n        val, nseconds = divmod(nseconds, offset)\n        if val:\n            suffix = val != 1 and 's' or ''\n            accum.append('%s %s%s' % (val, name, suffix))\n    if not accum:\n        return '0 seconds'\n    return glue.join(accum)\n\n@udf(FILE)\ndef file_ext(filename):\n    try:\n        res = os.path.splitext(filename)\n    except ValueError:\n        return None\n    return res[1]\n\n@udf(FILE)\ndef file_read(filename):\n    try:\n        with open(filename) as fh:\n            return fh.read()\n    except:\n        pass\n\n@udf(HELPER)\ndef gzip(data, compression=9):\n    if isinstance(data, str):\n        data = bytes(data.encode('raw_unicode_escape'))\n    return zlib.compress(data, compression)\n\n@udf(HELPER)\ndef gunzip(data):\n    return zlib.decompress(data)\n\n@udf(HELPER)\ndef hostname(url):\n    parse_result = urlparse(url)\n    if parse_result:\n        return parse_result.netloc\n\n@udf(HELPER)\ndef toggle(key):\n    key = key.lower()\n    STATE[key] = ret = not STATE.get(key)\n    return ret\n\n@udf(HELPER)\ndef setting(key, value=None):\n    if value is None:\n        return SETTINGS.get(key)\n    else:\n        SETTINGS[key] = value\n        return value\n\n@udf(HELPER)\ndef clear_settings():\n    SETTINGS.clear()\n\n@udf(HELPER)\ndef clear_toggles():\n    STATE.clear()\n\n@udf(MATH)\ndef randomrange(start, end=None, step=None):\n    if end is None:\n        start, end = 0, start\n    elif step is None:\n        step = 1\n    return random.randrange(start, end, step)\n\n@udf(MATH)\ndef gauss_distribution(mean, sigma):\n    try:\n        return random.gauss(mean, sigma)\n    except ValueError:\n        return None\n\n@udf(MATH)\ndef sqrt(n):\n    try:\n        return math.sqrt(n)\n    except ValueError:\n        return None\n\n@udf(MATH)\ndef tonumber(s):\n    try:\n        return int(s)\n    except ValueError:\n        try:\n            return float(s)\n        except:\n            return None\n\n@udf(STRING)\ndef substr_count(haystack, needle):\n    if not haystack or not needle:\n        return 0\n    return haystack.count(needle)\n\n@udf(STRING)\ndef strip_chars(haystack, chars):\n    return haystack.strip(chars)\n\n@udf(JSON)\ndef json_contains(src_json, obj_json):\n    stack = []\n    try:\n        stack.append((json.loads(obj_json), json.loads(src_json)))\n    except:\n        # Invalid JSON!\n        return False\n\n    while stack:\n        obj, src = stack.pop()\n        if isinstance(src, dict):\n            if isinstance(obj, dict):\n                for key in obj:\n                    if key not in src:\n                        return False\n                    stack.append((obj[key], src[key]))\n            elif isinstance(obj, list):\n                for item in obj:\n                    if item not in src:\n                        return False\n            elif obj not in src:\n                return False\n        elif isinstance(src, list):\n            if isinstance(obj, dict):\n                return False\n            elif isinstance(obj, list):\n                try:\n                    for i in range(len(obj)):\n                        stack.append((obj[i], src[i]))\n                except IndexError:\n                    return False\n            elif obj not in src:\n                return False\n        elif obj != src:\n            return False\n    return True\n\n\n# Aggregates.\nclass _heap_agg(object):\n    def __init__(self):\n        self.heap = []\n        self.ct = 0\n\n    def process(self, value):\n        return value\n\n    def step(self, value):\n        self.ct += 1\n        heapq.heappush(self.heap, self.process(value))\n\nclass _datetime_heap_agg(_heap_agg):\n    def process(self, value):\n        return format_date_time_sqlite(value)\n\n@aggregate(DATE)\nclass mintdiff(_datetime_heap_agg):\n    def finalize(self):\n        dtp = min_diff = None\n        while self.heap:\n            if min_diff is None:\n                if dtp is None:\n                    dtp = heapq.heappop(self.heap)\n                    continue\n            dt = heapq.heappop(self.heap)\n            diff = dt - dtp\n            if min_diff is None or min_diff > diff:\n                min_diff = diff\n            dtp = dt\n        if min_diff is not None:\n            return min_diff.total_seconds()\n\n@aggregate(DATE)\nclass avgtdiff(_datetime_heap_agg):\n    def finalize(self):\n        if self.ct < 1:\n            return\n        elif self.ct == 1:\n            return 0\n\n        total = ct = 0\n        dtp = None\n        while self.heap:\n            if total == 0:\n                if dtp is None:\n                    dtp = heapq.heappop(self.heap)\n                    continue\n\n            dt = heapq.heappop(self.heap)\n            diff = dt - dtp\n            ct += 1\n            total += diff.total_seconds()\n            dtp = dt\n\n        return float(total) / ct\n\n@aggregate(DATE)\nclass duration(object):\n    def __init__(self):\n        self._min = self._max = None\n\n    def step(self, value):\n        dt = format_date_time_sqlite(value)\n        if self._min is None or dt < self._min:\n            self._min = dt\n        if self._max is None or dt > self._max:\n            self._max = dt\n\n    def finalize(self):\n        if self._min and self._max:\n            td = (self._max - self._min)\n            return td.total_seconds()\n        return None\n\n@aggregate(MATH)\nclass mode(object):\n    def __init__(self):\n        self.items = collections.Counter()\n\n    def step(self, *args):\n        self.items.update(args)\n\n    def finalize(self):\n        if self.items:\n            return self.items.most_common(1)[0][0]\n\n@aggregate(MATH)\nclass minrange(_heap_agg):\n    def finalize(self):\n        if self.ct == 0:\n            return\n        elif self.ct == 1:\n            return 0\n\n        prev = min_diff = None\n\n        while self.heap:\n            if min_diff is None:\n                if prev is None:\n                    prev = heapq.heappop(self.heap)\n                    continue\n            curr = heapq.heappop(self.heap)\n            diff = curr - prev\n            if min_diff is None or min_diff > diff:\n                min_diff = diff\n            prev = curr\n        return min_diff\n\n@aggregate(MATH)\nclass avgrange(_heap_agg):\n    def finalize(self):\n        if self.ct == 0:\n            return\n        elif self.ct == 1:\n            return 0\n\n        total = ct = 0\n        prev = None\n        while self.heap:\n            if total == 0:\n                if prev is None:\n                    prev = heapq.heappop(self.heap)\n                    continue\n\n            curr = heapq.heappop(self.heap)\n            diff = curr - prev\n            ct += 1\n            total += diff\n            prev = curr\n\n        return float(total) / ct\n\n@aggregate(MATH)\nclass _range(object):\n    name = 'range'\n\n    def __init__(self):\n        self._min = self._max = None\n\n    def step(self, value):\n        if self._min is None or value < self._min:\n            self._min = value\n        if self._max is None or value > self._max:\n            self._max = value\n\n    def finalize(self):\n        if self._min is not None and self._max is not None:\n            return self._max - self._min\n        return None\n\n@aggregate(MATH)\nclass stddev(object):\n    def __init__(self):\n        self.n = 0\n        self.values = []\n    def step(self, v):\n        self.n += 1\n        self.values.append(v)\n    def finalize(self):\n        if self.n <= 1:\n            return 0\n        mean = sum(self.values) / self.n\n        return math.sqrt(sum((i - mean) ** 2 for i in self.values) / (self.n - 1))\n\n\ndef _parse_match_info(buf):\n    # See http://sqlite.org/fts3.html#matchinfo\n    bufsize = len(buf)  # Length in bytes.\n    return [struct.unpack('@I', buf[i:i+4])[0] for i in range(0, bufsize, 4)]\n\ndef get_weights(ncol, raw_weights):\n    if not raw_weights:\n        return [1] * ncol\n    else:\n        weights = [0] * ncol\n        for i, weight in enumerate(raw_weights):\n            weights[i] = weight\n    return weights\n\n# Ranking implementation, which parse matchinfo.\ndef rank(raw_match_info, *raw_weights):\n    # Handle match_info called w/default args 'pcx' - based on the example rank\n    # function http://sqlite.org/fts3.html#appendix_a\n    match_info = _parse_match_info(raw_match_info)\n    score = 0.0\n\n    p, c = match_info[:2]\n    weights = get_weights(c, raw_weights)\n\n    # matchinfo X value corresponds to, for each phrase in the search query, a\n    # list of 3 values for each column in the search table.\n    # So if we have a two-phrase search query and three columns of data, the\n    # following would be the layout:\n    # p0 : c0=[0, 1, 2],   c1=[3, 4, 5],    c2=[6, 7, 8]\n    # p1 : c0=[9, 10, 11], c1=[12, 13, 14], c2=[15, 16, 17]\n    for phrase_num in range(p):\n        phrase_info_idx = 2 + (phrase_num * c * 3)\n        for col_num in range(c):\n            weight = weights[col_num]\n            if not weight:\n                continue\n\n            col_idx = phrase_info_idx + (col_num * 3)\n\n            # The idea is that we count the number of times the phrase appears\n            # in this column of the current row, compared to how many times it\n            # appears in this column across all rows. The ratio of these values\n            # provides a rough way to score based on \"high value\" terms.\n            row_hits = match_info[col_idx]\n            all_rows_hits = match_info[col_idx + 1]\n            if row_hits > 0:\n                score += weight * (float(row_hits) / all_rows_hits)\n\n    return -score\n\n# Okapi BM25 ranking implementation (FTS4 only).\ndef bm25(raw_match_info, *args):\n    \"\"\"\n    Usage:\n\n        # Format string *must* be pcnalx\n        # Second parameter to bm25 specifies the index of the column, on\n        # the table being queries.\n        bm25(matchinfo(document_tbl, 'pcnalx'), 1) AS rank\n    \"\"\"\n    match_info = _parse_match_info(raw_match_info)\n    K = 1.2\n    B = 0.75\n    score = 0.0\n\n    P_O, C_O, N_O, A_O = range(4)  # Offsets into the matchinfo buffer.\n    term_count = match_info[P_O]  # n\n    col_count = match_info[C_O]\n    total_docs = match_info[N_O]  # N\n    L_O = A_O + col_count\n    X_O = L_O + col_count\n\n    # Worked example of pcnalx for two columns and two phrases, 100 docs total.\n    # {\n    #   p  = 2\n    #   c  = 2\n    #   n  = 100\n    #   a0 = 4   -- avg number of tokens for col0, e.g. title\n    #   a1 = 40  -- avg number of tokens for col1, e.g. body\n    #   l0 = 5   -- curr doc has 5 tokens in col0\n    #   l1 = 30  -- curr doc has 30 tokens in col1\n    #\n    #   x000     -- hits this row for phrase0, col0\n    #   x001     -- hits all rows for phrase0, col0\n    #   x002     -- rows with phrase0 in col0 at least once\n    #\n    #   x010     -- hits this row for phrase0, col1\n    #   x011     -- hits all rows for phrase0, col1\n    #   x012     -- rows with phrase0 in col1 at least once\n    #\n    #   x100     -- hits this row for phrase1, col0\n    #   x101     -- hits all rows for phrase1, col0\n    #   x102     -- rows with phrase1 in col0 at least once\n    #\n    #   x110     -- hits this row for phrase1, col1\n    #   x111     -- hits all rows for phrase1, col1\n    #   x112     -- rows with phrase1 in col1 at least once\n    # }\n\n    weights = get_weights(col_count, args)\n\n    for i in range(term_count):\n        for j in range(col_count):\n            weight = weights[j]\n            if weight == 0:\n                continue\n\n            x = X_O + (3 * (j + i * col_count))\n            term_frequency = float(match_info[x])  # f(qi, D)\n            docs_with_term = float(match_info[x + 2])  # n(qi)\n\n            # log( (N - n(qi) + 0.5) / (n(qi) + 0.5) )\n            idf = math.log(\n                    (total_docs - docs_with_term + 0.5) /\n                    (docs_with_term + 0.5))\n            if idf <= 0.0:\n                idf = 1e-6\n\n            doc_length = float(match_info[L_O + j])  # |D|\n            avg_length = float(match_info[A_O + j]) or 1.  # avgdl\n            ratio = doc_length / avg_length\n\n            num = term_frequency * (K + 1.0)\n            b_part = 1.0 - B + (B * ratio)\n            denom = term_frequency + (K * b_part)\n\n            pc_score = idf * (num / denom)\n            score += (pc_score * weight)\n\n    return -score\n\n\nif cython_udf is not None:\n    rank = udf(RANK, 'fts_rank')(cython_udf.peewee_rank)\n    lucene = udf(RANK, 'fts_lucene')(cython_udf.peewee_lucene)\n    bm25 = udf(RANK, 'fts_bm25')(cython_udf.peewee_bm25)\n    bm25f = udf(RANK, 'fts_bm25f')(cython_udf.peewee_bm25f)\n\n    damerau_levenshtein_dist = udf(STRING)(cython_udf.damerau_levenshtein_dist)\n    levenshtein_dist = udf(STRING)(cython_udf.levenshtein_dist)\n    str_dist = udf(STRING)(cython_udf.str_dist)\n    median = aggregate(MATH)(cython_udf.median)\nelse:\n    rank = udf(RANK, 'fts_rank')(rank)\n    bm25 = udf(RANK, 'fts_bm25')(bm25)\n"
  },
  {
    "path": "playhouse/sqliteq.py",
    "content": "import logging\nimport weakref\nfrom queue import Queue\nfrom threading import local as thread_local\nfrom threading import Event\nfrom threading import Lock\nfrom threading import Thread\n\ntry:\n    import gevent\n    from gevent import Greenlet as GThread\n    from gevent.event import Event as GEvent\n    from gevent.local import local as greenlet_local\n    from gevent.queue import Queue as GQueue\nexcept ImportError:\n    GThread = GQueue = GEvent = None\n\nfrom peewee import SqliteDatabase\n\n\nlogger = logging.getLogger('peewee.sqliteq')\n\n\nclass ResultTimeout(Exception):\n    pass\n\nclass WriterPaused(Exception):\n    pass\n\nclass ShutdownException(Exception):\n    pass\n\n\nclass AsyncCursor(object):\n    __slots__ = ('sql', 'params', 'timeout',\n                 '_event', '_cursor', '_exc', '_idx', '_rows', '_ready')\n\n    def __init__(self, event, sql, params, timeout):\n        self._event = event\n        self.sql = sql\n        self.params = params\n        self.timeout = timeout\n        self._cursor = self._exc = self._idx = self._rows = None\n        self._ready = False\n\n    def set_result(self, cursor, exc=None):\n        self._cursor = cursor\n        self._exc = exc\n        self._idx = 0\n        self._rows = cursor.fetchall() if exc is None else []\n        self._event.set()\n        return self\n\n    def _wait(self, timeout=None):\n        timeout = timeout if timeout is not None else self.timeout\n        if not self._event.wait(timeout=timeout) and timeout is not None:\n            raise ResultTimeout('results not ready, timed out.')\n        if self._exc is not None:\n            raise self._exc\n        self._ready = True\n\n    def __iter__(self):\n        if not self._ready:\n            self._wait()\n        if self._exc is not None:\n            raise self._exc\n        return self\n\n    def next(self):\n        if not self._ready:\n            self._wait()\n        try:\n            obj = self._rows[self._idx]\n        except IndexError:\n            raise StopIteration\n        else:\n            self._idx += 1\n            return obj\n    __next__ = next\n\n    @property\n    def lastrowid(self):\n        if not self._ready:\n            self._wait()\n        return self._cursor.lastrowid\n\n    @property\n    def rowcount(self):\n        if not self._ready:\n            self._wait()\n        return self._cursor.rowcount\n\n    @property\n    def description(self):\n        if not self._ready:\n            self._wait()\n        return self._cursor.description\n\n    def close(self):\n        if self._cursor is not None:\n            self._cursor.close()\n            self._cursor = None\n\n    def fetchall(self):\n        return list(self)  # Iterating implies waiting until populated.\n\n    def fetchone(self):\n        if not self._ready:\n            self._wait()\n        try:\n            return next(self)\n        except StopIteration:\n            return None\n\nSHUTDOWN = StopIteration\nQUERY = object()\nPAUSE = object()\nUNPAUSE = object()\n\n\nclass Writer(object):\n    __slots__ = ('database', 'queue')\n\n    def __init__(self, database, queue):\n        self.database = database\n        self.queue = queue\n\n    def run(self):\n        conn = self.database.connection()\n        try:\n            while True:\n                try:\n                    if conn is None:  # Paused.\n                        if self.wait_unpause():\n                            conn = self.database.connection()\n                    else:\n                        conn = self.loop(conn)\n                except ShutdownException:\n                    logger.info('writer received shutdown request, exiting.')\n                    return\n        finally:\n            if conn is not None:\n                self.database._close(conn)\n                self.database._state.reset()\n\n    def wait_unpause(self):\n        op, obj = self.queue.get()\n        if op is UNPAUSE:\n            logger.info('writer unpaused - reconnecting to database.')\n            obj.set()\n            return True\n        elif op is SHUTDOWN:\n            raise ShutdownException()\n        elif op is PAUSE:\n            logger.error('writer received pause, but is already paused.')\n            obj.set()\n        else:\n            obj.set_result(None, WriterPaused())\n            logger.warning('writer paused, not handling %s', obj)\n\n    def loop(self, conn):\n        op, obj = self.queue.get()\n        if op is QUERY:\n            self.execute(obj)\n        elif op is PAUSE:\n            logger.info('writer paused - closing database connection.')\n            self.database._close(conn)\n            self.database._state.reset()\n            obj.set()\n            return\n        elif op is UNPAUSE:\n            logger.error('writer received unpause, but is already running.')\n            obj.set()\n        elif op is SHUTDOWN:\n            raise ShutdownException()\n        else:\n            logger.error('writer received unsupported object: %s', obj)\n        return conn\n\n    def execute(self, obj):\n        logger.debug('received query %s', obj.sql)\n        try:\n            cursor = self.database._execute(obj.sql, obj.params)\n        except Exception as execute_err:\n            cursor = None\n            exc = execute_err  # python3 is so fucking lame.\n        else:\n            exc = None\n        return obj.set_result(cursor, exc)\n\n\nclass SqliteQueueDatabase(SqliteDatabase):\n    WAL_MODE_ERROR_MESSAGE = ('SQLite must be configured to use the WAL '\n                              'journal mode when using this feature. WAL mode '\n                              'allows one or more readers to continue reading '\n                              'while another connection writes to the '\n                              'database.')\n\n    def __init__(self, database, use_gevent=False, autostart=True,\n                 queue_max_size=None, results_timeout=None, *args, **kwargs):\n        kwargs['check_same_thread'] = False\n\n        # Lock around starting and stopping write thread operations.\n        self._qlock = Lock()\n\n        # Ensure that journal_mode is WAL. This value is passed to the parent\n        # class constructor below.\n        pragmas = self._validate_journal_mode(kwargs.pop('pragmas', None))\n\n        # Reference to execute_sql on the parent class. Since we've overridden\n        # execute_sql(), this is just a handy way to reference the real\n        # implementation.\n        Parent = super(SqliteQueueDatabase, self)\n        self._execute = Parent.execute_sql\n\n        # Call the parent class constructor with our modified pragmas.\n        Parent.__init__(database, pragmas=pragmas, *args, **kwargs)\n\n        self._autostart = autostart\n        self._results_timeout = results_timeout\n        self._is_stopped = True\n\n        # Get different objects depending on the threading implementation.\n        self._thread_helper = self.get_thread_impl(use_gevent)(queue_max_size)\n\n        # Create the writer thread, optionally starting it.\n        self._create_write_queue()\n        if self._autostart:\n            self.start()\n\n    def get_thread_impl(self, use_gevent):\n        return GreenletHelper if use_gevent else ThreadHelper\n\n    def _validate_journal_mode(self, pragmas=None):\n        if not pragmas:\n            return {'journal_mode': 'wal'}\n\n        if not isinstance(pragmas, dict):\n            pragmas = dict((k.lower(), v) for (k, v) in pragmas)\n        if pragmas.get('journal_mode', 'wal').lower() != 'wal':\n            raise ValueError(self.WAL_MODE_ERROR_MESSAGE)\n\n        pragmas['journal_mode'] = 'wal'\n        return pragmas\n\n    def _create_write_queue(self):\n        self._write_queue = self._thread_helper.queue()\n\n    def queue_size(self):\n        return self._write_queue.qsize()\n\n    def execute_sql(self, sql, params=None, timeout=None):\n        if sql.lower().startswith('select'):\n            return self._execute(sql, params)\n\n        cursor = AsyncCursor(\n            event=self._thread_helper.event(),\n            sql=sql,\n            params=params,\n            timeout=self._results_timeout if timeout is None else timeout)\n        self._write_queue.put((QUERY, cursor))\n        return cursor\n\n    def start(self):\n        with self._qlock:\n            if not self._is_stopped:\n                return False\n            def run():\n                writer = Writer(self, self._write_queue)\n                writer.run()\n\n            self._writer = self._thread_helper.thread(run)\n            self._writer.start()\n            self._is_stopped = False\n            return True\n\n    def stop(self):\n        logger.debug('environment stop requested.')\n        with self._qlock:\n            if self._is_stopped:\n                return False\n\n            self._write_queue.put((SHUTDOWN, None))\n            writer = self._writer\n            self._is_stopped = True\n\n        writer.join()\n\n        # Empty queue of any remaining tasks.\n        while not self._write_queue.empty():\n            op, obj = self._write_queue.get()\n            if op is PAUSE or op is UNPAUSE:\n                obj.set()\n            elif op is QUERY:\n                obj.set_result(None, ShutdownException())\n\n            return True\n\n    def is_stopped(self):\n        with self._qlock:\n            return self._is_stopped\n\n    def pause(self):\n        with self._qlock:\n            if self._is_stopped:\n                return False\n\n            evt = self._thread_helper.event()\n            self._write_queue.put((PAUSE, evt))\n\n        evt.wait()\n\n    def unpause(self):\n        with self._qlock:\n            if self._is_stopped:\n                return False\n\n            evt = self._thread_helper.event()\n            self._write_queue.put((UNPAUSE, evt))\n\n        evt.wait()\n\n    def __unsupported__(self, *args, **kwargs):\n        raise ValueError('This method is not supported by %r.' % type(self))\n    atomic = transaction = savepoint = __unsupported__\n\n\nclass ThreadHelper(object):\n    __slots__ = ('queue_max_size',)\n\n    def __init__(self, queue_max_size=None):\n        self.queue_max_size = queue_max_size\n\n    def event(self): return Event()\n\n    def queue(self, max_size=None):\n        max_size = max_size if max_size is not None else self.queue_max_size\n        return Queue(maxsize=max_size or 0)\n\n    def thread(self, fn, *args, **kwargs):\n        thread = Thread(target=fn, args=args, kwargs=kwargs)\n        thread.daemon = True\n        return thread\n\n\nclass GreenletHelper(ThreadHelper):\n    __slots__ = ()\n\n    def event(self): return GEvent()\n\n    def queue(self, max_size=None):\n        max_size = max_size if max_size is not None else self.queue_max_size\n        return GQueue(maxsize=max_size or 0)\n\n    def thread(self, fn, *args, **kwargs):\n        def wrap(*a, **k):\n            gevent.sleep()\n            return fn(*a, **k)\n        return GThread(wrap, *args, **kwargs)\n"
  },
  {
    "path": "playhouse/test_utils.py",
    "content": "from functools import wraps\nimport logging\n\n\nlogger = logging.getLogger('peewee')\n\n\nclass _QueryLogHandler(logging.Handler):\n    def __init__(self, *args, **kwargs):\n        self.queries = []\n        logging.Handler.__init__(self, *args, **kwargs)\n\n    def emit(self, record):\n        # Counts all entries logged to the \"peewee\" logger by execute_sql().\n        if record.name == 'peewee':\n            self.queries.append(record)\n\n\nclass count_queries(object):\n    def __init__(self, only_select=False):\n        self.only_select = only_select\n        self.count = 0\n\n    def get_queries(self):\n        return self._handler.queries\n\n    def __enter__(self):\n        self._handler = _QueryLogHandler()\n        logger.setLevel(logging.DEBUG)\n        logger.addHandler(self._handler)\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        logger.removeHandler(self._handler)\n        if self.only_select:\n            self.count = len([q for q in self._handler.queries\n                              if q.msg[0].startswith('SELECT ')])\n        else:\n            self.count = len(self._handler.queries)\n\n\nclass assert_query_count(count_queries):\n    def __init__(self, expected, only_select=False):\n        super(assert_query_count, self).__init__(only_select=only_select)\n        self.expected = expected\n\n    def __call__(self, f):\n        @wraps(f)\n        def decorated(*args, **kwds):\n            with self:\n                ret = f(*args, **kwds)\n\n            self._assert_count()\n            return ret\n\n        return decorated\n\n    def _assert_count(self):\n        error_msg = '%s != %s' % (self.count, self.expected)\n        assert self.count == self.expected, error_msg\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        super(assert_query_count, self).__exit__(exc_type, exc_val, exc_tb)\n        self._assert_count()\n"
  },
  {
    "path": "pwiz.py",
    "content": "#!/usr/bin/env python\n\nimport datetime\nimport os\nimport sys\nfrom getpass import getpass\nfrom optparse import OptionParser\n\nfrom peewee import *\nfrom peewee import __version__ as peewee_version\nfrom playhouse.cockroachdb import CockroachDatabase\nfrom playhouse.reflection import *\n\n\nHEADER = \"\"\"from peewee import *%s\n\ndatabase = %s('%s'%s)\n\"\"\"\n\nBASE_MODEL = \"\"\"\\\nclass BaseModel(Model):\n    class Meta:\n        database = database\n\"\"\"\n\nUNKNOWN_FIELD = \"\"\"\\\nclass UnknownField(object):\n    def __init__(self, *_, **__): pass\n\"\"\"\n\nDATABASE_ALIASES = {\n    CockroachDatabase: ['cockroach', 'cockroachdb', 'crdb'],\n    MySQLDatabase: ['mysql', 'mysqldb'],\n    PostgresqlDatabase: ['postgres', 'postgresql'],\n    SqliteDatabase: ['sqlite', 'sqlite3'],\n}\n\nDATABASE_MAP = dict((value, key)\n                    for key in DATABASE_ALIASES\n                    for value in DATABASE_ALIASES[key])\n\ndef make_introspector(database_type, database_name, **kwargs):\n    if database_type not in DATABASE_MAP:\n        err('Unrecognized database, must be one of: %s' %\n            ', '.join(DATABASE_MAP.keys()))\n        sys.exit(1)\n\n    schema = kwargs.pop('schema', None)\n    DatabaseClass = DATABASE_MAP[database_type]\n    db = DatabaseClass(database_name, **kwargs)\n    return Introspector.from_database(db, schema=schema)\n\ndef print_models(introspector, tables=None, preserve_order=False,\n                 include_views=False, ignore_unknown=False, snake_case=True):\n    database = introspector.introspect(table_names=tables,\n                                       include_views=include_views,\n                                       snake_case=snake_case)\n\n    db_kwargs = introspector.get_database_kwargs()\n    header = HEADER % (\n        introspector.get_additional_imports(),\n        introspector.get_database_class().__name__,\n        introspector.get_database_name().replace('\\\\', '\\\\\\\\'),\n        ', **%s' % repr(db_kwargs) if db_kwargs else '')\n    print(header)\n\n    if not ignore_unknown:\n        print(UNKNOWN_FIELD)\n\n    print(BASE_MODEL)\n\n    def _print_table(table, seen, accum=None):\n        accum = accum or []\n        foreign_keys = database.foreign_keys[table]\n        for foreign_key in foreign_keys:\n            dest = foreign_key.dest_table\n\n            # In the event the destination table has already been pushed\n            # for printing, then we have a reference cycle.\n            if dest in accum and table not in accum:\n                print('# Possible reference cycle: %s' % dest)\n\n            # If this is not a self-referential foreign key, and we have\n            # not already processed the destination table, do so now.\n            if dest not in seen and dest not in accum:\n                seen.add(dest)\n                if dest != table:\n                    _print_table(dest, seen, accum + [table])\n\n        print('class %s(BaseModel):' % database.model_names[table])\n        columns = database.columns[table].items()\n        if not preserve_order:\n            columns = sorted(columns)\n        primary_keys = database.primary_keys[table]\n        for name, column in columns:\n            skip = all([\n                name in primary_keys,\n                name == 'id',\n                len(primary_keys) == 1,\n                column.field_class in introspector.pk_classes])\n            if skip:\n                continue\n            if column.primary_key and len(primary_keys) > 1:\n                # If we have a CompositeKey, then we do not want to explicitly\n                # mark the columns as being primary keys.\n                column.primary_key = False\n\n            is_unknown = column.field_class is UnknownField\n            if is_unknown and ignore_unknown:\n                disp = '%s - %s' % (column.name, column.raw_column_type or '?')\n                print('    # %s' % disp)\n            else:\n                print('    %s' % column.get_field())\n\n        print('')\n        print('    class Meta:')\n        print('        table_name = \\'%s\\'' % table)\n        multi_column_indexes = database.multi_column_indexes(table)\n        if multi_column_indexes:\n            print('        indexes = (')\n            for fields, unique in sorted(multi_column_indexes):\n                print('            ((%s), %s),' % (\n                    ', '.join(\"'%s'\" % field for field in fields),\n                    unique,\n                ))\n            print('        )')\n\n        if introspector.schema:\n            print('        schema = \\'%s\\'' % introspector.schema)\n        if len(primary_keys) > 1:\n            pk_field_names = sorted([\n                field.name for col, field in columns\n                if col in primary_keys])\n            pk_list = ', '.join(\"'%s'\" % pk for pk in pk_field_names)\n            print('        primary_key = CompositeKey(%s)' % pk_list)\n        elif not primary_keys:\n            print('        primary_key = False')\n        print('')\n\n        seen.add(table)\n\n    seen = set()\n    for table in sorted(database.model_names.keys()):\n        if table not in seen:\n            if not tables or table in tables:\n                _print_table(table, seen)\n\ndef print_header(cmd_line, introspector):\n    timestamp = datetime.datetime.now()\n    print('# Code generated by:')\n    print('# python -m pwiz %s' % cmd_line)\n    print('# Date: %s' % timestamp.strftime('%B %d, %Y %I:%M%p'))\n    print('# Database: %s' % introspector.get_database_name())\n    print('# Peewee version: %s' % peewee_version)\n    print('')\n\n\ndef err(msg):\n    sys.stderr.write('\\033[91m%s\\033[0m\\n' % msg)\n    sys.stderr.flush()\n\ndef get_option_parser():\n    parser = OptionParser(usage='usage: %prog [options] database_name')\n    ao = parser.add_option\n    ao('-H', '--host', dest='host')\n    ao('-p', '--port', dest='port', type='int')\n    ao('-u', '--user', dest='user')\n    ao('-P', '--password', dest='password', action='store_true')\n    engines = sorted(DATABASE_MAP)\n    ao('-e', '--engine', dest='engine', choices=engines,\n       help=('Database type, e.g. sqlite, mysql, postgresql or cockroachdb. '\n             'Default is \"postgresql\".'))\n    ao('-s', '--schema', dest='schema')\n    ao('-t', '--tables', dest='tables',\n       help=('Only generate the specified tables. Multiple table names should '\n             'be separated by commas.'))\n    ao('-v', '--views', dest='views', action='store_true',\n       help='Generate model classes for VIEWs in addition to tables.')\n    ao('-i', '--info', dest='info', action='store_true',\n       help=('Add database information and other metadata to top of the '\n             'generated file.'))\n    ao('-o', '--preserve-order', action='store_true', dest='preserve_order',\n       help='Model definition column ordering matches source table.')\n    ao('-I', '--ignore-unknown', action='store_true', dest='ignore_unknown',\n       help='Ignore fields whose type cannot be determined.')\n    ao('-L', '--legacy-naming', action='store_true', dest='legacy_naming',\n       help='Use legacy table- and column-name generation.')\n    return parser\n\ndef get_connect_kwargs(options):\n    ops = ('host', 'port', 'user', 'schema')\n    kwargs = dict((o, getattr(options, o)) for o in ops if getattr(options, o))\n    if options.password:\n        kwargs['password'] = getpass()\n    return kwargs\n\ndef main():\n    raw_argv = sys.argv\n\n    parser = get_option_parser()\n    options, args = parser.parse_args()\n\n    if len(args) < 1:\n        err('Missing required parameter \"database\"')\n        parser.print_help()\n        sys.exit(1)\n\n    connect = get_connect_kwargs(options)\n    database = args[-1]\n\n    tables = None\n    if options.tables:\n        tables = [table.strip() for table in options.tables.split(',')\n                  if table.strip()]\n\n    engine = options.engine\n    if engine is None:\n        engine = 'sqlite' if os.path.exists(database) else 'postgresql'\n\n    introspector = make_introspector(engine, database, **connect)\n    if options.info:\n        cmd_line = ' '.join(raw_argv[1:])\n        print_header(cmd_line, introspector)\n\n    print_models(introspector, tables, options.preserve_order, options.views,\n                 options.ignore_unknown, not options.legacy_naming)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools\", \"wheel\", \"cython\"]\nbuild-backend=\"setuptools.build_meta\"\n\n[project]\nname = \"peewee\"\ndynamic = [\"version\"]\ndescription = \"a little orm\"\nreadme = \"README.rst\"\nauthors = [\n    { name = \"Charles Leifer\", email = \"coleifer@gmail.com\" }\n]\nclassifiers = [\n    \"Development Status :: 5 - Production/Stable\",\n    \"Intended Audience :: Developers\",\n    \"Operating System :: OS Independent\",\n    \"Programming Language :: Python :: 3\",\n    \"Topic :: Database\",\n    \"Topic :: Software Development :: Libraries :: Python Modules\",\n]\n\n[project.scripts]\npwiz = \"pwiz:main\"\n\n[project.urls]\nRepository = \"https://github.com/coleifer/peewee\"\nDocumentation = \"https://docs.peewee-orm.com/\"\nChangelog = \"https://github.com/coleifer/peewee/blob/master/CHANGELOG.md\"\n\n[project.optional-dependencies]\nmysql = [\"pymysql\"]\npostgres = [\"psycopg2-binary\"]\npsycopg3 = [\"psycopg[binary]\"]\ncysqlite = [\"cysqlite\"]\naiosqlite = [\"aiosqlite \", \"greenlet\"]\naiomysql = [\"aiomysql\", \"greenlet\"]\nasyncpg = [\"asyncpg \", \"greenlet\"]\n\n[tool.setuptools]\npackages = [\"playhouse\"]\npy-modules = [\"peewee\", \"pwiz\"]\nexclude-package-data = {\"playhouse\" = [\"*.pyx\"]}\n\n[tool.setuptools.dynamic]\nversion = { attr = \"peewee.__version__\" }\n"
  },
  {
    "path": "runtests.py",
    "content": "#!/usr/bin/env python\nimport optparse\nimport os\nimport shutil\nimport sys\nimport unittest\n\n\nUSER = os.environ.get('USER') or 'root'\n\n\ndef runtests(suite, verbosity=1, failfast=False):\n    runner = unittest.TextTestRunner(verbosity=verbosity, failfast=failfast)\n    results = runner.run(suite)\n    return results.failures, results.errors\n\ndef get_option_parser():\n    usage = 'usage: %prog [-e engine_name, other options] module1, module2 ...'\n    parser = optparse.OptionParser(usage=usage)\n    basic = optparse.OptionGroup(parser, 'Basic test options')\n    basic.add_option(\n        '-e',\n        '--engine',\n        dest='engine',\n        help=('Database engine to test, one of '\n              '[sqlite, postgres, mysql, mysqlconnector, apsw, sqlcipher,'\n              ' cockroachdb, psycopg3]'))\n    basic.add_option('-v', '--verbosity', dest='verbosity', default=1,\n                     type='int', help='Verbosity of output')\n    basic.add_option('-f', '--failfast', action='store_true', default=False,\n                     dest='failfast', help='Exit on first failure/error.')\n    basic.add_option('-s', '--slow-tests', action='store_true', default=False,\n                     dest='slow_tests', help='Run tests that may be slow.')\n    basic.add_option('-a', '--asyncio', action='store_true', default=False,\n                     dest='asyncio_tests', help='Run only asyncio tests.')\n    basic.add_option('-A', '--asyncio-stress', action='store_true',\n                     default=False, dest='asyncio_stress_test',\n                     help='Run asyncio stress test.')\n    parser.add_option_group(basic)\n\n    db_param_map = (\n        ('MySQL', 'MYSQL', (\n            # param  default disp default val\n            ('host', 'localhost', 'localhost'),\n            ('port', '3306', ''),\n            ('user', USER, USER),\n            ('password', 'blank', ''))),\n        ('Postgresql', 'PSQL', (\n            ('host', 'localhost', os.environ.get('PGHOST', '')),\n            ('port', '5432', ''),\n            ('user', 'postgres', os.environ.get('PGUSER', '')),\n            ('password', 'blank', os.environ.get('PGPASSWORD', '')))),\n        ('CockroachDB', 'CRDB', (\n            # param  default disp default val\n            ('host', 'localhost', 'localhost'),\n            ('port', '26257', ''),\n            ('user', 'root', 'root'),\n            ('password', 'blank', ''))))\n    for name, prefix, param_list in db_param_map:\n        group = optparse.OptionGroup(parser, '%s connection options' % name)\n        for param, default_disp, default_val in param_list:\n            dest = '%s_%s' % (prefix.lower(), param)\n            opt = '--%s-%s' % (prefix.lower(), param)\n            group.add_option(opt, default=default_val, dest=dest, help=(\n                '%s database %s. Default %s.' % (name, param, default_disp)))\n\n        parser.add_option_group(group)\n    return parser\n\ndef collect_tests(args):\n    suite = unittest.TestSuite()\n\n    if not args:\n        import tests\n        module_suite = unittest.TestLoader().loadTestsFromModule(tests)\n        suite.addTest(module_suite)\n    else:\n        cleaned = ['tests.%s' % arg if not arg.startswith('tests.') else arg\n                   for arg in args]\n        user_suite = unittest.TestLoader().loadTestsFromNames(cleaned)\n        suite.addTest(user_suite)\n\n    return suite\n\ndef collect_asyncio_tests():\n    try:\n        import aiosqlite\n    except ImportError:\n        raise RuntimeError('Need aiosqlite at minimum to run asyncio tests.')\n\n    suite = unittest.TestSuite()\n    from tests import pwasyncio\n    module_suite = unittest.TestLoader().loadTestsFromModule(pwasyncio)\n    suite.addTest(module_suite)\n    return suite\n\n\nif __name__ == '__main__':\n    parser = get_option_parser()\n    options, args = parser.parse_args()\n\n    if options.engine:\n        os.environ['PEEWEE_TEST_BACKEND'] = options.engine\n\n    for db in ('mysql', 'psql', 'crdb'):\n        for key in ('host', 'port', 'user', 'password'):\n            att_name = '_'.join((db, key))\n            value = getattr(options, att_name, None)\n            if value:\n                os.environ['PEEWEE_%s' % att_name.upper()] = value\n\n    os.environ['PEEWEE_TEST_VERBOSITY'] = str(options.verbosity)\n    if options.slow_tests:\n        os.environ['PEEWEE_SLOW_TESTS'] = '1'\n\n    if options.asyncio_tests:\n        suite = collect_asyncio_tests()\n    elif options.asyncio_stress_test:\n        try:\n            import asyncio\n            from tests.pwasyncio_stress import main\n        except ImportError as exc:\n            print('Error: could not import asyncio stress test: %s' % exc)\n            sys.exit(2)\n\n        sys.exit(asyncio.run(main()))\n    else:\n        suite = collect_tests(args)\n\n    failures, errors = runtests(suite, options.verbosity, options.failfast)\n\n    files_to_delete = [\n        'peewee_test.db',\n        'peewee_test',\n        'tmp.db',\n        'peewee_test.bdb.db',\n        'peewee_test.cipher.db']\n    paths_to_delete = ['peewee_test.bdb.db-journal']\n    for filename in files_to_delete:\n        if os.path.exists(filename):\n            os.unlink(filename)\n    for path in paths_to_delete:\n        if os.path.exists(path):\n            shutil.rmtree(path)\n\n    if errors:\n        sys.exit(2)\n    elif failures:\n        sys.exit(1)\n\n    sys.exit(0)\n"
  },
  {
    "path": "setup.py",
    "content": "import platform\nimport os\nfrom setuptools import setup\nfrom setuptools.extension import Extension\ntry:\n    from Cython.Build import cythonize\n    cython_installed = True\nexcept ImportError:\n    cython_installed = False\n\n\nif platform.python_implementation() != 'CPython':\n    extension_support = False\nelif os.environ.get('NO_SQLITE'):\n    # Retain backward-compat for not building C extensions.\n    extension_support = False\nelse:\n    extension_support = True\n\nif cython_installed:\n    src_ext = '.pyx'\nelse:\n    src_ext = '.c'\n    cythonize = lambda obj: obj\n\nif extension_support:\n    sqlite_udf_module = Extension(\n        'playhouse._sqlite_udf',\n        ['playhouse/_sqlite_udf' + src_ext])\n    ext_modules = cythonize([sqlite_udf_module])\nelse:\n    ext_modules = []\n\nsetup(\n    name='peewee',\n    packages=['playhouse'],\n    py_modules=['peewee', 'pwiz'],\n    ext_modules=ext_modules)\n"
  },
  {
    "path": "tests/__init__.py",
    "content": "import sys\nimport unittest\n\nfrom peewee import OperationalError\n\n# Core modules.\nfrom .db_tests import *\nfrom .expressions import *\nfrom .fields import *\nfrom .keys import *\nfrom .manytomany import *\nfrom .models import *\nfrom .model_save import *\nfrom .model_sql import *\nfrom .prefetch_tests import *\nfrom .queries import *\nfrom .regressions import *\nfrom .results import *\nfrom .schema import *\nfrom .sql import *\nfrom .transactions import *\n\n# Extensions.\ntry:\n    from .apsw_ext import *\nexcept ImportError:\n    print('Unable to import APSW extension tests, skipping.')\ntry:\n    from .cockroachdb import *\nexcept:\n    print('Unable to import CockroachDB tests, skipping.')\ntry:\n    from .cysqlite_ext import *\nexcept ImportError:\n    print('Unable to import cysqlite tests, skipping.')\nfrom .dataset import *\nfrom .db_url import *\nfrom .extra_fields import *\nfrom .hybrid import *\nfrom .kv import *\nfrom .migrations import *\ntry:\n    import mysql.connector\n    from .mysql_ext import *\nexcept ImportError:\n    print('Unable to import mysql-connector, skipping mysql_ext tests.')\nfrom .pool import *\ntry:\n    from .postgres import *\nexcept (ImportError, ImproperlyConfigured):\n    print('Unable to import postgres extension tests, skipping.')\nexcept OperationalError:\n    print('Postgresql test database \"peewee_test\" not found, skipping '\n          'the postgres_ext tests.')\nfrom .pwiz_integration import *\nfrom .reflection import *\nfrom .returning import *\nfrom .shortcuts import *\nfrom .signals import *\ntry:\n    from .sqlcipher_ext import *\nexcept ImportError:\n    print('Unable to import SQLCipher extension tests, skipping.')\ntry:\n    from .sqlite import *\nexcept ImportError:\n    print('Unable to import sqlite extension tests, skipping.')\ntry:\n    from .sqlite_changelog import *\nexcept ImportError:\n    print('Unable to import sqlite changelog tests, skipping.')\nfrom .sqliteq import *\nfrom .sqlite_udf import *\nfrom .test_utils import *\n\ntry:\n    from .pwasyncio import *\nexcept (ImportError, SyntaxError):\n    print('Unable to import asyncio tests, skipping.')\n\ntry:\n    from .pydantic_utils import *\nexcept (ImportError, SyntaxError):\n    print('Unable to import pydantic tests, skipping.')\n"
  },
  {
    "path": "tests/__main__.py",
    "content": "import os\nimport sys\nimport unittest\n\nsrc_dir = os.path.realpath(os.path.dirname(os.path.dirname(__file__)))\nsys.path.insert(0, src_dir)\nfrom tests import *\n\n\nif __name__ == '__main__':\n    print('\\x1b[1;31m')\n    print(r\"\"\"\n     ______   ______     ______     __     __     ______     ______\n    /\\  == \\ /\\  ___\\   /\\  ___\\   /\\ \\  _ \\ \\   /\\  ___\\   /\\  ___\\\n    \\ \\  _-/ \\ \\  __\\   \\ \\  __\\   \\ \\ \\/ \".\\ \\  \\ \\  __\\   \\ \\  __\\\n     \\ \\_\\    \\ \\_____\\  \\ \\_____\\  \\ \\__/\".~\\_\\  \\ \\_____\\  \\ \\_____\\\n      \\/_/     \\/_____/   \\/_____/   \\/_/   \\/_/   \\/_____/   \\/_____/\n    \"\"\")\n    print('\\x1b[1;33m')\n    print(r\"\"\"\n                           _                 _\n                       _  /\\ \\             / /\\\n                      /\\_\\\\ \\ \\           / /  \\\n                     / / / \\ \\ \\         / / /\\ \\\n                    / / /   \\ \\ \\       / / /\\ \\ \\\n                    \\ \\ \\____\\ \\ \\     /_/ /  \\ \\ \\\n                     \\ \\________\\ \\    \\ \\ \\   \\ \\ \\\n                      \\/________/\\ \\    \\ \\ \\   \\ \\ \\\n                                \\ \\ \\  _ \\ \\ \\___\\ \\ \\\n                                 \\ \\_\\/\\_\\\\ \\/____\\ \\ \\\n                                  \\/_/\\/_/ \\_________\\/\n    \"\"\")\n    print('\\x1b[0m')\n    unittest.main(argv=sys.argv)\n"
  },
  {
    "path": "tests/apsw_ext.py",
    "content": "import apsw\nimport datetime\n\nfrom playhouse.apsw_ext import *\nfrom .base import ModelTestCase\nfrom .base import TestModel\n\n\ndatabase = APSWDatabase(':memory:')\n\n\nclass User(TestModel):\n    username = TextField()\n\n\nclass Message(TestModel):\n    user = ForeignKeyField(User)\n    message = TextField()\n    pub_date = DateTimeField()\n    published = BooleanField()\n\n\nclass VTSource(object):\n    def Create(self, db, modulename, dbname, tablename, *args):\n        schema = 'CREATE TABLE x(value)'\n        return schema, VTable()\n    Connect = Create\nclass VTable(object):\n    def BestIndex(self, *args):\n        return\n    def Open(self):\n        return VTCursor()\n    def Disconnect(self): pass\n    Destroy = Disconnect\nclass VTCursor(object):\n    def Filter(self, *a):\n        self.val = 0\n    def Eof(self): return False\n    def Rowid(self):\n        return self.val\n    def Column(self, col):\n        return self.val\n    def Next(self):\n        self.val += 1\n    def Close(self): pass\n\n\nclass TestAPSWExtension(ModelTestCase):\n    database = database\n    requires = [User, Message]\n\n    def test_db_register_module(self):\n        database.register_module('series', VTSource())\n        database.execute_sql('create virtual table foo using series()')\n        curs = database.execute_sql('select * from foo limit 5;')\n        self.assertEqual([v for v, in curs], [0, 1, 2, 3, 4])\n        database.unregister_module('series')\n\n    def test_db_register_function(self):\n        @database.func()\n        def title(s):\n            return s.title()\n\n        curs = self.database.execute_sql('SELECT title(?)', ('heLLo',))\n        self.assertEqual(curs.fetchone()[0], 'Hello')\n\n    def test_db_register_aggregate(self):\n        @database.aggregate()\n        class First(object):\n            def __init__(self):\n                self._value = None\n\n            def step(self, value):\n                if self._value is None:\n                    self._value = value\n\n            def finalize(self):\n                return self._value\n\n        with database.atomic():\n            for i in range(10):\n                User.create(username='u%s' % i)\n\n        query = User.select(fn.First(User.username)).order_by(User.username)\n        self.assertEqual(query.scalar(), 'u0')\n\n    def test_db_register_collation(self):\n        @database.collation()\n        def reverse(lhs, rhs):\n            lhs, rhs = lhs.lower(), rhs.lower()\n            if lhs < rhs:\n                return 1\n            return -1 if rhs > lhs else 0\n\n        with database.atomic():\n            for i in range(3):\n                User.create(username='u%s' % i)\n\n        query = (User\n                 .select(User.username)\n                 .order_by(User.username.collate('reverse')))\n        self.assertEqual([u.username for u in query], ['u2', 'u1', 'u0'])\n\n    def test_db_pragmas(self):\n        test_db = APSWDatabase(':memory:', pragmas=(\n            ('cache_size', '1337'),\n        ))\n        test_db.connect()\n\n        cs = test_db.execute_sql('PRAGMA cache_size;').fetchone()[0]\n        self.assertEqual(cs, 1337)\n\n    def test_select_insert(self):\n        for user in ('u1', 'u2', 'u3'):\n            User.create(username=user)\n\n        self.assertEqual([x.username for x in User.select()], ['u1', 'u2', 'u3'])\n\n        dt = datetime.datetime(2012, 1, 1, 11, 11, 11)\n        Message.create(user=User.get(User.username == 'u1'), message='herps', pub_date=dt, published=True)\n        Message.create(user=User.get(User.username == 'u2'), message='derps', pub_date=dt, published=False)\n\n        m1 = Message.get(Message.message == 'herps')\n        self.assertEqual(m1.user.username, 'u1')\n        self.assertEqual(m1.pub_date, dt)\n        self.assertEqual(m1.published, True)\n\n        m2 = Message.get(Message.message == 'derps')\n        self.assertEqual(m2.user.username, 'u2')\n        self.assertEqual(m2.pub_date, dt)\n        self.assertEqual(m2.published, False)\n\n    def test_update_delete(self):\n        u1 = User.create(username='u1')\n        u2 = User.create(username='u2')\n\n        u1.username = 'u1-modified'\n        u1.save()\n\n        self.assertEqual(User.select().count(), 2)\n        self.assertEqual(User.get(User.username == 'u1-modified').id, u1.id)\n\n        u1.delete_instance()\n        self.assertEqual(User.select().count(), 1)\n\n    def test_transaction_handling(self):\n        dt = datetime.datetime(2012, 1, 1, 11, 11, 11)\n\n        def do_ctx_mgr_error():\n            with self.database.transaction():\n                User.create(username='u1')\n                raise ValueError\n\n        self.assertRaises(ValueError, do_ctx_mgr_error)\n        self.assertEqual(User.select().count(), 0)\n\n        def do_ctx_mgr_success():\n            with self.database.transaction():\n                u = User.create(username='test')\n                Message.create(message='testing', user=u, pub_date=dt, published=1)\n\n        do_ctx_mgr_success()\n        self.assertEqual(User.select().count(), 1)\n        self.assertEqual(Message.select().count(), 1)\n\n        def create_error():\n            with self.database.atomic():\n                u = User.create(username='test')\n                Message.create(message='testing', user=u, pub_date=dt,\n                               published=1)\n                raise ValueError\n\n        self.assertRaises(ValueError, create_error)\n        self.assertEqual(User.select().count(), 1)\n\n        def create_success():\n            with self.database.atomic():\n                u = User.create(username='test')\n                Message.create(message='testing', user=u, pub_date=dt,\n                               published=1)\n\n        create_success()\n        self.assertEqual(User.select().count(), 2)\n        self.assertEqual(Message.select().count(), 2)\n\n    def test_exists_regression(self):\n        User.create(username='u1')\n        self.assertTrue(User.select().where(User.username == 'u1').exists())\n        self.assertFalse(User.select().where(User.username == 'ux').exists())\n"
  },
  {
    "path": "tests/base.py",
    "content": "from contextlib import contextmanager\nfrom functools import wraps\nimport datetime\nimport logging\nimport os\nimport re\nimport unittest\n\nfrom peewee import *\nfrom peewee import sqlite3\nfrom playhouse.cockroachdb import CockroachDatabase\nfrom playhouse.cockroachdb import NESTED_TX_MIN_VERSION\nfrom playhouse.mysql_ext import MariaDBConnectorDatabase\nfrom playhouse.mysql_ext import MySQLConnectorDatabase\ntry:\n    from playhouse.cysqlite_ext import CySqliteDatabase\nexcept ImportError:\n    CySqliteDatabase = None\n\n\nlogger = logging.getLogger('peewee')\n\n\ndef db_loader(engine, name='peewee_test', db_class=None, **params):\n    if db_class is None:\n        engine_aliases = {\n            SqliteDatabase: ['sqlite', 'sqlite3'],\n            CySqliteDatabase: ['cysqlite'],\n            MySQLDatabase: ['mysql'],\n            PostgresqlDatabase: ['postgres', 'postgresql', 'psycopg3'],\n            MySQLConnectorDatabase: ['mysqlconnector'],\n            MariaDBConnectorDatabase: ['mariadb', 'maridbconnector'],\n            CockroachDatabase: ['cockroach', 'cockroachdb', 'crdb'],\n        }\n        engine_map = dict((alias, db) for db, aliases in engine_aliases.items()\n                          for alias in aliases if db is not None)\n        if engine.lower() not in engine_map:\n            raise Exception('Unsupported engine: %s.' % engine)\n        db_class = engine_map[engine.lower()]\n    if issubclass(db_class, SqliteDatabase) and not name.endswith('.db'):\n        name = '%s.db' % name if name != ':memory:' else name\n    elif issubclass(db_class, MySQLDatabase):\n        params.update(MYSQL_PARAMS)\n    elif issubclass(db_class, CockroachDatabase):\n        params.update(CRDB_PARAMS)\n    elif issubclass(db_class, PostgresqlDatabase):\n        params.update(PSQL_PARAMS)\n\n    return db_class(name, **params)\n\n\ndef get_in_memory_db(**params):\n    backend = 'cysqlite' if BACKEND == 'cysqlite' else 'sqlite3'\n    return db_loader(backend, ':memory:', **params)\n\ndef get_sqlite_db():\n    backend = 'cysqlite' if BACKEND == 'cysqlite' else 'sqlite3'\n    return db_loader(backend)\n\n\nBACKEND = os.environ.get('PEEWEE_TEST_BACKEND') or 'sqlite'\nVERBOSITY = int(os.environ.get('PEEWEE_TEST_VERBOSITY') or 1)\nSLOW_TESTS = bool(os.environ.get('PEEWEE_SLOW_TESTS'))\n\n# What family of database are we using.\nIS_SQLITE = BACKEND.startswith(('sqlite', 'cysqlite'))\nIS_MYSQL = BACKEND.startswith(('mysql', 'maria'))\nIS_POSTGRESQL = BACKEND.startswith(('postgres', 'psycopg'))\n\n# Specific database or driver.\nIS_CRDB = BACKEND in ('cockroach', 'cockroachdb', 'crdb')\nIS_PSYCOPG3 = BACKEND == 'psycopg3'\nIS_CYSQLITE = BACKEND == 'cysqlite'\n\nif IS_MYSQL:\n    try:\n        import pymysql\n    except ImportError:\n        raise ImportError('pymysql is not installed')\nif BACKEND.startswith('postgres'):\n    try:\n        import psycopg2\n    except ImportError:\n        raise ImportError('psycopg2 is not installed')\nif IS_PSYCOPG3:\n    try:\n        import psycopg\n    except ImportError:\n        raise ImportError('psycopg3 is not installed')\n\n\ndef make_db_params(key):\n    params = {}\n    env_vars = [(part, 'PEEWEE_%s_%s' % (key, part.upper()))\n                for part in ('host', 'port', 'user', 'password')]\n    for param, env_var in env_vars:\n        value = os.environ.get(env_var)\n        if value:\n            params[param] = int(value) if param == 'port' else value\n    return params\n\nCRDB_PARAMS = make_db_params('CRDB')\nMYSQL_PARAMS = make_db_params('MYSQL')\nPSQL_PARAMS = make_db_params('PSQL')\nif IS_PSYCOPG3:\n    PSQL_PARAMS['prefer_psycopg3'] = True\n\nif VERBOSITY > 1:\n    handler = logging.StreamHandler()\n    handler.setLevel(logging.INFO)\n    logger.addHandler(handler)\nif VERBOSITY > 2:\n    handler.setLevel(logging.DEBUG)\n\n\ndef new_connection(**kwargs):\n    return db_loader(BACKEND, 'peewee_test', **kwargs)\n\n\ndb = new_connection()\n\n\n# Database-specific feature flags.\nIS_SQLITE_OLD = IS_SQLITE and sqlite3.sqlite_version_info < (3, 18)\nIS_SQLITE_15 = IS_SQLITE and sqlite3.sqlite_version_info >= (3, 15)\nIS_SQLITE_24 = IS_SQLITE and sqlite3.sqlite_version_info >= (3, 24)\nIS_SQLITE_25 = IS_SQLITE and sqlite3.sqlite_version_info >= (3, 25)\nIS_SQLITE_30 = IS_SQLITE and sqlite3.sqlite_version_info >= (3, 30)\nIS_SQLITE_35 = IS_SQLITE and sqlite3.sqlite_version_info >= (3, 35)\nIS_SQLITE_37 = IS_SQLITE and sqlite3.sqlite_version_info >= (3, 37)\nIS_SQLITE_9 = IS_SQLITE and sqlite3.sqlite_version_info >= (3, 9)\nIS_MYSQL_ADVANCED_FEATURES = False\nIS_MYSQL_JSON = False\nif IS_MYSQL:\n    db.connect()\n    server_info = db.server_version\n    if server_info[0] == 8 or server_info[:2] >= (10, 2):\n        IS_MYSQL_ADVANCED_FEATURES = True\n    elif server_info[0] == 0:\n        logger.warning('Could not determine mysql server version.')\n    if server_info[0] >= 8 or ((5, 7) <= server_info[:2] <= (6, 0)):\n        # Needs actual MySQL - not MariaDB.\n        IS_MYSQL_JSON = True\n    db.close()\n    if not IS_MYSQL_ADVANCED_FEATURES:\n        logger.warning('MySQL too old to test certain advanced features.')\n\nif IS_CRDB:\n    db.connect()\n    IS_CRDB_NESTED_TX = db.server_version >= NESTED_TX_MIN_VERSION\n    db.close()\nelse:\n    IS_CRDB_NESTED_TX = False\n\n\nclass TestModel(Model):\n    class Meta:\n        database = db\n        legacy_table_names = False\n\n\ndef __sql__(q, **state):\n    return Context(**state).sql(q).query()\n\n\nclass QueryLogHandler(logging.Handler):\n    def __init__(self, *args, **kwargs):\n        self.queries = []\n        logging.Handler.__init__(self, *args, **kwargs)\n\n    def emit(self, record):\n        self.queries.append(record)\n\n\nclass BaseTestCase(unittest.TestCase):\n    def setUp(self):\n        self._qh = QueryLogHandler()\n        logger.setLevel(logging.DEBUG)\n        logger.addHandler(self._qh)\n\n    def tearDown(self):\n        logger.removeHandler(self._qh)\n\n    def assertIsNone(self, value):\n        self.assertTrue(value is None, '%r is not None' % value)\n\n    def assertIsNotNone(self, value):\n        self.assertTrue(value is not None, '%r is None' % value)\n\n    @contextmanager\n    def assertRaisesCtx(self, exceptions):\n        try:\n            yield\n        except Exception as exc:\n            if not isinstance(exc, exceptions):\n                raise AssertionError('Got %s, expected %s' % (exc, exceptions))\n        else:\n            raise AssertionError('No exception was raised.')\n\n    def assertSQL(self, query, sql, params=None, **state):\n        database = getattr(self, 'database', None) or db\n        state.setdefault('conflict_statement', database.conflict_statement)\n        state.setdefault('conflict_update', database.conflict_update)\n        qsql, qparams = __sql__(query, **state)\n        self.assertEqual(qsql, sql)\n        if params is not None:\n            self.assertEqual(qparams, params)\n\n    def assertHistory(self, n, expected):\n        queries = [logrecord.msg for logrecord in self._qh.queries[-n:]]\n        queries = [(sql.replace('%s', '?').replace('`', '\"'), params)\n                   for sql, params in queries]\n        self.assertEqual(queries, expected)\n\n    @property\n    def history(self):\n        return self._qh.queries\n\n    def reset_sql_history(self):\n        self._qh.queries = []\n\n    @contextmanager\n    def assertQueryCount(self, num):\n        qc = len(self.history)\n        yield\n        self.assertEqual(len(self.history) - qc, num)\n\n\nclass DatabaseTestCase(BaseTestCase):\n    database = db\n\n    def setUp(self):\n        if not self.database.is_closed():\n            self.database.close()\n        self.database.connect()\n        super(DatabaseTestCase, self).setUp()\n\n    def tearDown(self):\n        super(DatabaseTestCase, self).tearDown()\n        self.database.close()\n\n    def execute(self, sql, params=None):\n        return self.database.execute_sql(sql, params)\n\n\nclass ModelDatabaseTestCase(DatabaseTestCase):\n    database = db\n    requires = None\n\n    def setUp(self):\n        super(ModelDatabaseTestCase, self).setUp()\n        self._db_mapping = {}\n        # Override the model's database object with test db.\n        if self.requires:\n            for model in self.requires:\n                self._db_mapping[model] = model._meta.database\n                model._meta.set_database(self.database)\n\n    def tearDown(self):\n        # Restore the model's previous database object.\n        if self.requires:\n            for model in self.requires:\n                model._meta.set_database(self._db_mapping[model])\n\n        super(ModelDatabaseTestCase, self).tearDown()\n\n\nclass ModelTestCase(ModelDatabaseTestCase):\n    database = db\n    requires = None\n\n    def setUp(self):\n        super(ModelTestCase, self).setUp()\n        if self.requires:\n            self.database.drop_tables(self.requires, safe=True)\n            self.database.create_tables(self.requires)\n\n    def tearDown(self):\n        # Restore the model's previous database object.\n        try:\n            if self.requires:\n                self.database.drop_tables(self.requires, safe=True)\n        finally:\n            super(ModelTestCase, self).tearDown()\n\n\ndef requires_models(*models):\n    def decorator(method):\n        @wraps(method)\n        def inner(self):\n            with self.database.bind_ctx(models, False, False):\n                self.database.drop_tables(models, safe=True)\n                self.database.create_tables(models)\n\n                try:\n                    method(self)\n                finally:\n                    try:\n                        self.database.drop_tables(models)\n                    except:\n                        pass\n        return inner\n    return decorator\n\n\ndef skip_if(expr, reason='n/a'):\n    def decorator(method):\n        return unittest.skipIf(expr, reason)(method)\n    return decorator\n\ndef skip_unless(expr, reason='n/a'):\n    def decorator(method):\n        return unittest.skipUnless(expr, reason)(method)\n    return decorator\n\ndef slow_test():\n    def decorator(method):\n        return unittest.skipUnless(SLOW_TESTS, 'skipping slow test')(method)\n    return decorator\n\ndef requires_sqlite(method):\n    return skip_unless(IS_SQLITE, 'requires sqlite')(method)\n\ndef requires_mysql(method):\n    return skip_unless(IS_MYSQL, 'requires mysql')(method)\n\ndef requires_postgresql(method):\n    return skip_unless(IS_POSTGRESQL, 'requires postgresql')(method)\n\ndef requires_pglike(method):\n    return skip_unless(IS_POSTGRESQL or IS_CRDB, 'requires pg-like')(method)\n"
  },
  {
    "path": "tests/base_models.py",
    "content": "from peewee import *\n\nfrom .base import TestModel\n\n\nclass Person(TestModel):\n    first = CharField()\n    last = CharField()\n    dob = DateField(index=True)\n\n    class Meta:\n        indexes = (\n            (('first', 'last'), True),\n        )\n\n\nclass Note(TestModel):\n    author = ForeignKeyField(Person)\n    content = TextField()\n\n\nclass Category(TestModel):\n    parent = ForeignKeyField('self', backref='children', null=True)\n    name = CharField(max_length=20, primary_key=True)\n\n\nclass Relationship(TestModel):\n    from_person = ForeignKeyField(Person, backref='relations')\n    to_person = ForeignKeyField(Person, backref='related_to')\n\n\nclass Register(TestModel):\n    value = IntegerField()\n\n\nclass User(TestModel):\n    username = CharField()\n\n    class Meta:\n        table_name = 'users'\n\n\nclass Account(TestModel):\n    email = CharField()\n    user = ForeignKeyField(User, backref='accounts', null=True)\n\n\nclass Tweet(TestModel):\n    user = ForeignKeyField(User, backref='tweets')\n    content = TextField()\n    timestamp = TimestampField()\n\n\nclass Favorite(TestModel):\n    user = ForeignKeyField(User, backref='favorites')\n    tweet = ForeignKeyField(Tweet, backref='favorites')\n\n\nclass Sample(TestModel):\n    counter = IntegerField()\n    value = FloatField(default=1.0)\n\n\nclass SampleMeta(TestModel):\n    sample = ForeignKeyField(Sample, backref='metadata')\n    value = FloatField(default=0.0)\n\n\nclass A(TestModel):\n    a = TextField()\nclass B(TestModel):\n    a = ForeignKeyField(A, backref='bs')\n    b = TextField()\nclass C(TestModel):\n    b = ForeignKeyField(B, backref='cs')\n    c = TextField()\n\n\nclass Emp(TestModel):\n    first = CharField()\n    last = CharField()\n    empno = CharField(unique=True)\n\n    class Meta:\n        indexes = (\n            (('first', 'last'), True),\n        )\n\n\nclass OCTest(TestModel):\n    a = CharField(unique=True)\n    b = IntegerField(default=0)\n    c = IntegerField(default=0)\n\n\nclass UKVP(TestModel):\n    key = TextField()\n    value = IntegerField()\n    extra = IntegerField()\n\n    class Meta:\n        # Partial index, the WHERE clause must be reflected in the conflict\n        # target.\n        indexes = [\n            SQL('CREATE UNIQUE INDEX \"ukvp_kve\" ON \"ukvp\" (\"key\", \"value\") '\n                'WHERE \"extra\" > 1')]\n\n\nclass DfltM(TestModel):\n    name = CharField()\n    dflt1 = IntegerField(default=1)\n    dflt2 = IntegerField(default=lambda: 2)\n    dfltn = IntegerField(null=True)\n"
  },
  {
    "path": "tests/cockroachdb.py",
    "content": "import datetime\nimport uuid\n\nfrom peewee import *\nfrom playhouse.cockroachdb import *\n\nfrom .base import IS_CRDB\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db\nfrom .base import requires_models\nfrom .base import skip_unless\nfrom .base_models import User\nfrom .postgres_helpers import BaseBinaryJsonFieldTestCase\n\n\nclass KV(TestModel):\n    k = TextField(unique=True)\n    v = IntegerField()\n\nclass Arr(TestModel):\n    title = TextField()\n    tags = ArrayField(TextField, index=False)\n\nclass JsonModel(TestModel):\n    data = JSONField()\n\nclass Normal(TestModel):\n    data = TextField()\n\nclass UID(TestModel):\n    id = UUIDKeyField()\n    title = TextField()\n\nclass RID(TestModel):\n    id = RowIDField()\n    title = TextField()\n\nclass UIDNote(TestModel):\n    uid = ForeignKeyField(UID, backref='notes')\n    note = TextField()\n\n\n@skip_unless(IS_CRDB)\nclass TestCockroachDatabase(ModelTestCase):\n    @requires_models(KV)\n    def test_retry_transaction_ok(self):\n        @self.database.retry_transaction()\n        def succeeds(db):\n            k1 = KV.create(k='k1', v=1)\n            k2 = KV.create(k='k2', v=2)\n            return [k1.id, k2.id]\n\n        id_list = succeeds()\n        self.assertEqual(KV.select().count(), 2)\n\n        kv_list = [kv.id for kv in KV.select().order_by(KV.k)]\n        self.assertEqual(kv_list, id_list)\n\n    @requires_models(KV)\n    def test_retry_transfer_example(self):\n        k1 = KV.create(k='k1', v=100)\n        k2 = KV.create(k='k2', v=1)\n\n        def transfer_funds(from_k, to_k, amt):\n            query = KV.select().where(KV.k.in_((from_k, to_k)))\n            ka, kb = list(query)\n            if from_k != ka.k:\n                ka, kb = kb, ka  # Swap order.\n            if ka.v < amt:\n                return False, ka.v, kb.v\n            from_v, = (KV\n                       .update(v=KV.v - amt)\n                       .where(KV.k == from_k)\n                       .returning(KV.v)\n                       .execute())\n            to_v, = (KV\n                     .update(v=KV.v + amt)\n                     .where(KV.k == to_k)\n                     .returning(KV.v)\n                     .execute())\n            return True, from_v.v, to_v.v\n\n        def thunk(db_ref):\n            return transfer_funds('k1', 'k2', 90)\n        self.assertEqual(run_transaction(self.database, thunk), (True, 10, 91))\n\n        def thunk(db_ref):\n            return transfer_funds('k1', 'k2', 5)\n        self.assertEqual(run_transaction(self.database, thunk), (True, 5, 96))\n\n        def thunk(db_ref):\n            return transfer_funds('k1', 'k2', 6)\n        self.assertEqual(run_transaction(self.database, thunk), (False, 5, 96))\n\n    @requires_models(KV)\n    def test_retry_transfer_example2(self):\n        k1 = KV.create(k='k1', v=100)\n        k2 = KV.create(k='k2', v=1)\n\n        def transfer_funds(from_k, to_k, amount):\n            def thunk(db_ref):\n                src, dest = KV.select().where(KV.k.in_([from_k, to_k]))\n                if src.k != from_k:\n                    src, dest = dest, src\n                if src.v < amount:\n                    return False, src.v, dest.v\n                src, = (KV\n                        .update(v=KV.v - amount)\n                        .where(KV.k == from_k)\n                        .returning(KV.v)\n                        .execute())\n                dest, = (KV\n                         .update(v=KV.v + amount)\n                         .where(KV.k == to_k)\n                         .returning(KV.v)\n                         .execute())\n                return True, src.v, dest.v\n            return run_transaction(self.database, thunk, max_attempts=10)\n\n        self.assertEqual(transfer_funds('k1', 'k2', 90), (True, 10, 91))\n        self.assertEqual(transfer_funds('k1', 'k2', 11), (False, 10, 91))\n        self.assertEqual(transfer_funds('k1', 'k2', 10), (True, 0, 101))\n\n    @requires_models(KV)\n    def test_retry_transaction_integrityerror(self):\n        KV.create(k='kx', v=0)\n\n        @self.database.retry_transaction()\n        def fails(db):\n            KV.create(k='k1', v=1)\n            KV.create(k='kx', v=1)\n\n        with self.assertRaises(IntegrityError):\n            fails()\n\n        self.assertEqual(KV.select().count(), 1)\n        kv = KV.get(KV.k == 'kx')\n        self.assertEqual(kv.v, 0)\n\n    @requires_models(KV)\n    def test_run_transaction_helper(self):\n        def succeeds(db):\n            KV.insert_many([('k%s' % i, i) for i in range(10)]).execute()\n        run_transaction(self.database, succeeds)\n        self.assertEqual([(kv.k, kv.v) for kv in KV.select().order_by(KV.k)],\n                         [('k%s' % i, i) for i in range(10)])\n\n    @requires_models(KV)\n    def test_cannot_nest_run_transaction(self):\n        def insert_row(db):\n            KV.create(k='k1', v=1)\n\n        with self.database.atomic():\n            self.assertRaises(Exception, run_transaction,\n                              self.database, insert_row)\n        self.assertEqual(KV.select().count(), 0)\n\n    @requires_models(User)\n    def test_retry_transaction_docs_example(self):\n        def create_user(username):\n            def thunk(db_ref):\n                return User.create(username=username)\n            return self.database.run_transaction(thunk, max_attempts=5)\n\n        users = [create_user(u) for u in 'abc']\n        self.assertEqual([u.username for u in users], ['a', 'b', 'c'])\n\n        query = User.select().order_by(User.username)\n        self.assertEqual([u.username for u in query], ['a', 'b', 'c'])\n\n    @requires_models(KV)\n    def test_retry_transaction_decorator(self):\n        @self.database.retry_transaction()\n        def retry_decorator(db):\n            content = []\n            for i in range(5):\n                kv = KV.create(k='k%s' % i, v=i)\n                content.append(kv.k)\n            return content\n\n        self.assertEqual(retry_decorator(), ['k0', 'k1', 'k2', 'k3', 'k4'])\n\n    @requires_models(Arr)\n    def test_array_field(self):\n        a1 = Arr.create(title='a1', tags=['t1', 't2'])\n        a2 = Arr.create(title='a2', tags=['t2', 't3'])\n\n        # Ensure we can read an array back.\n        a1_db = Arr.get(Arr.title == 'a1')\n        self.assertEqual(a1_db.tags, ['t1', 't2'])\n\n        # Ensure we can filter on arrays.\n        a2_db = Arr.get(Arr.tags == ['t2', 't3'])\n        self.assertEqual(a2_db.id, a2.id)\n\n        # Item lookups.\n        a1_db = Arr.get(Arr.tags[1] == 't2')\n        self.assertEqual(a1_db.id, a1.id)\n        self.assertRaises(Arr.DoesNotExist, Arr.get, Arr.tags[2] == 'x')\n\n    @requires_models(Arr)\n    def test_array_field_search(self):\n        def assertAM(where, id_list):\n            query = Arr.select().where(where).order_by(Arr.title)\n            self.assertEqual([a.id for a in query], id_list)\n\n        data = (\n            ('a1', ['t1', 't2']),\n            ('a2', ['t2', 't3']),\n            ('a3', ['t3', 't4']))\n        id_list = Arr.insert_many(data).execute()\n        a1, a2, a3 = [pk for pk, in id_list]\n\n        assertAM(Value('t2') == fn.ANY(Arr.tags), [a1, a2])\n        assertAM(Value('t1') == fn.Any(Arr.tags), [a1])\n        assertAM(Value('tx') == fn.Any(Arr.tags), [])\n\n        # Use the contains operator explicitly.\n        assertAM(SQL(\"tags::text[] @> ARRAY['t2']\"), [a1, a2])\n\n        # Use the porcelain.\n        assertAM(Arr.tags.contains('t2'), [a1, a2])\n        assertAM(Arr.tags.contains('t3'), [a2, a3])\n        assertAM(Arr.tags.contains('t1', 't2'), [a1])\n        assertAM(Arr.tags.contains('t3', 't4'), [a3])\n        assertAM(Arr.tags.contains('t2', 't3', 't4'), [])\n\n        assertAM(Arr.tags.contains_any('t2'), [a1, a2])\n        assertAM(Arr.tags.contains_any('t3'), [a2, a3])\n        assertAM(Arr.tags.contains_any('t1', 't2'), [a1, a2])\n        assertAM(Arr.tags.contains_any('t3', 't4'), [a2, a3])\n        assertAM(Arr.tags.contains_any('t2', 't3', 't4'), [a1, a2, a3])\n\n    @requires_models(Arr)\n    def test_array_field_index(self):\n        a1 = Arr.create(title='a1', tags=['a1', 'a2'])\n        a2 = Arr.create(title='a2', tags=['a2', 'a3', 'a4', 'a5'])\n\n        # NOTE: CRDB does not support array slicing.\n        query = (Arr\n                 .select(Arr.tags[1].alias('st'))\n                 .order_by(Arr.title))\n        self.assertEqual([a.st for a in query], ['a2', 'a3'])\n\n    @requires_models(UID)\n    def test_uuid_key_field(self):\n        # UUID primary-key is automatically populated and returned, and is of\n        # the correct type.\n        u1 = UID.create(title='u1')\n        self.assertTrue(u1.id is not None)\n        self.assertTrue(isinstance(u1.id, uuid.UUID))\n\n        # Bulk-insert works as expected.\n        id_list = UID.insert_many([('u2',), ('u3',)]).execute()\n        u2_id, u3_id = [pk for pk, in id_list]\n        self.assertTrue(isinstance(u2_id, uuid.UUID))\n\n        # We can perform lookups using UUID() type.\n        u2 = UID.get(UID.id == u2_id)\n        self.assertEqual(u2.title, 'u2')\n\n        # Get the UUID hex and query using that.\n        u3 = UID.get(UID.id == u3_id.hex)\n        self.assertEqual(u3.title, 'u3')\n\n    @requires_models(RID)\n    def test_rowid_field(self):\n        r1 = RID.create(title='r1')\n        self.assertTrue(r1.id is not None)\n\n        # Bulk-insert works as expected.\n        id_list = RID.insert_many([('r2',), ('r3',)]).execute()\n        r2_id, r3_id = [pk for pk, in id_list]\n\n        r2 = RID.get(RID.id == r2_id)\n        self.assertEqual(r2.title, 'r2')\n\n    @requires_models(KV)\n    def test_readonly_transaction(self):\n        kv = KV.create(k='k1', v=1)\n\n        # Table doesn't exist yet.\n        with self.assertRaises((ProgrammingError, InternalError)):\n            with self.database.atomic('-10s'):\n                kv_db = KV.get(KV.k == 'k1')\n\n        # Cannot write in a read-only transaction\n        with self.assertRaises((ProgrammingError, InternalError)):\n            with self.database.atomic(datetime.datetime.now()):\n                KV.create(k='k2', v=2)\n\n        # Without system time there are no issues.\n        with self.database.atomic():\n            kv_db = KV.get(KV.k == 'k1')\n            self.assertEqual(kv.id, kv_db.id)\n\n    @requires_models(KV)\n    def test_transaction_priority(self):\n        with self.database.atomic(priority='HIGH'):\n            KV.create(k='k1', v=1)\n        with self.assertRaises(IntegrityError):\n            with self.database.atomic(priority='LOW'):\n                KV.create(k='k1', v=2)\n        with self.assertRaises(ValueError):\n            with self.database.atomic(priority='HUH'):\n                KV.create(k='k2', v=2)\n\n        self.assertEqual(KV.select().count(), 1)\n        kv = KV.get()\n        self.assertEqual((kv.k, kv.v), ('k1', 1))\n\n    @requires_models(UID, UIDNote)\n    def test_uuid_key_as_fk(self):\n        # This is covered thoroughly elsewhere, but added here just for fun.\n        u1, u2, u3 = [UID.create(title='u%s' % i) for i in (1, 2, 3)]\n        UIDNote.create(uid=u1, note='u1-1')\n        UIDNote.create(uid=u2, note='u2-1')\n        UIDNote.create(uid=u2, note='u2-2')\n\n        with self.assertQueryCount(1):\n            query = (UIDNote\n                     .select(UIDNote, UID)\n                     .join(UID)\n                     .where(UID.title == 'u2')\n                     .order_by(UIDNote.note))\n            self.assertEqual([(un.note, un.uid.title) for un in query],\n                             [('u2-1', 'u2'), ('u2-2', 'u2')])\n\n        query = (UID\n                 .select(UID, fn.COUNT(UIDNote.id).alias('note_count'))\n                 .join(UIDNote, JOIN.LEFT_OUTER)\n                 .group_by(UID)\n                 .order_by(fn.COUNT(UIDNote.id).desc()))\n        self.assertEqual([(u.title, u.note_count) for u in query],\n                         [('u2', 2), ('u1', 1), ('u3', 0)])\n\n\n@skip_unless(IS_CRDB)\nclass TestCockroachDatabaseJson(BaseBinaryJsonFieldTestCase, ModelTestCase):\n    database = db\n    M = JsonModel\n    N = Normal\n    requires = [JsonModel, Normal]\n\n\n# General integration tests.\n\nclass KV2(TestModel):\n    k2 = CharField()\n    v2 = IntegerField()\n\nclass Post(TestModel):\n    content = TextField()\n    timestamp = DateTimeField(default=datetime.datetime.now)\n\nclass PostNote(TestModel):\n    post = ForeignKeyField(Post, backref='notes', primary_key=True)\n    note = TextField()\n\n\n@skip_unless(IS_CRDB)\nclass TestCockroachIntegration(ModelTestCase):\n    @requires_models(KV, KV2)\n    def test_compound_select(self):\n        KV.insert_many([('10', 1), ('40', 4)]).execute()\n        KV2.insert_many([('20', 2), ('30', 3)]).execute()\n\n        lhs = KV.select(KV.k.cast('INT'), KV.v)\n        rhs = KV2.select(KV2.k2.cast('INT'), KV2.v2)\n        query = (lhs | rhs).order_by(SQL('1'))\n        self.assertEqual([(obj.k, obj.v) for obj in query],\n                         [(10, 1), (20, 2), (30, 3), (40, 4)])\n\n    @requires_models(Post, PostNote)\n    def test_primary_key_as_foreign_key(self):\n        p = Post.create(content='p')\n        n = PostNote.create(post=p, note='n')\n\n        p_db = Post.select().get()\n        self.assertEqual([n.note for n in p_db.notes], ['n'])\n\n        with self.assertQueryCount(1):\n            query = (PostNote\n                     .select(PostNote, Post)\n                     .join(Post))\n            self.assertEqual([(n.post.content, n.note) for n in query],\n                             [('p', 'n')])\n\n\n@skip_unless(IS_CRDB)\nclass TestEnsureServerVersionSet(ModelTestCase):\n    # References GH ssue #2584.\n    requires = [KV]\n\n    def test_server_version_set(self):\n        # Mimic state of newly-initialized database.\n        self.database.close()\n        self.database.server_version = None\n\n        with self.database.atomic() as txn:\n            KV.create(k='k1', v=1)\n\n        self.assertTrue(self.database.server_version is not None)\n"
  },
  {
    "path": "tests/cysqlite_ext.py",
    "content": "import glob\nimport os\n\nimport cysqlite\n\nfrom peewee import *\nfrom playhouse.cysqlite_ext import *\n\nfrom .base import BaseTestCase\nfrom .base import DatabaseTestCase\nfrom .base import TestModel\nfrom .base import db_loader\nfrom .base import skip_unless\n\n\ndatabase = CySqliteDatabase('peewee_test.db', timeout=100)\n\n\nclass CyDatabaseTestCase(DatabaseTestCase):\n    database = database\n\n    def tearDown(self):\n        super(CyDatabaseTestCase, self).tearDown()\n        for filename in glob.glob(self.database.database + '*'):\n            os.unlink(filename)\n\n    def execute(self, sql, *params):\n        return self.database.execute_sql(sql, params)\n\n\nclass TestCSqliteHelpers(CyDatabaseTestCase):\n    def test_autocommit(self):\n        self.assertTrue(self.database.autocommit)\n        self.database.begin()\n        self.assertFalse(self.database.autocommit)\n        self.database.rollback()\n        self.assertTrue(self.database.autocommit)\n\n    def test_commit_hook(self):\n        state = {}\n\n        @self.database.on_commit\n        def on_commit():\n            state.setdefault('commits', 0)\n            state['commits'] += 1\n\n        self.execute('create table register (value text)')\n        self.assertEqual(state['commits'], 1)\n\n        # Check hook is preserved.\n        self.database.close()\n        self.database.connect()\n\n        self.execute('insert into register (value) values (?), (?)',\n                     'foo', 'bar')\n        self.assertEqual(state['commits'], 2)\n\n        curs = self.execute('select * from register order by value;')\n        results = curs.fetchall()\n        self.assertEqual([tuple(r) for r in results], [('bar',), ('foo',)])\n\n        self.assertEqual(state['commits'], 2)\n\n    def test_rollback_hook(self):\n        state = {}\n\n        @self.database.on_rollback\n        def on_rollback():\n            state.setdefault('rollbacks', 0)\n            state['rollbacks'] += 1\n\n        self.execute('create table register (value text);')\n        self.assertEqual(state, {})\n\n        # Check hook is preserved.\n        self.database.close()\n        self.database.connect()\n\n        self.database.begin()\n        self.execute('insert into register (value) values (?)', 'test')\n        self.database.rollback()\n        self.assertEqual(state, {'rollbacks': 1})\n\n        curs = self.execute('select * from register;')\n        self.assertEqual(curs.fetchall(), [])\n\n    def test_update_hook(self):\n        state = []\n\n        @self.database.on_update\n        def on_update(query, db, table, rowid):\n            state.append((query, db, table, rowid))\n\n        self.execute('create table register (value text)')\n        self.execute('insert into register (value) values (?), (?)',\n                     'foo', 'bar')\n\n        self.assertEqual(state, [\n            ('INSERT', 'main', 'register', 1),\n            ('INSERT', 'main', 'register', 2)])\n\n        # Check hook is preserved.\n        self.database.close()\n        self.database.connect()\n\n        self.execute('update register set value = ? where rowid = ?', 'baz', 1)\n        self.assertEqual(state, [\n            ('INSERT', 'main', 'register', 1),\n            ('INSERT', 'main', 'register', 2),\n            ('UPDATE', 'main', 'register', 1)])\n\n        self.execute('delete from register where rowid=?;', 2)\n        self.assertEqual(state, [\n            ('INSERT', 'main', 'register', 1),\n            ('INSERT', 'main', 'register', 2),\n            ('UPDATE', 'main', 'register', 1),\n            ('DELETE', 'main', 'register', 2)])\n\n    def test_properties(self):\n        self.assertTrue(self.database.cache_used is not None)\n\n\nclass TestBackup(CyDatabaseTestCase):\n    backup_filenames = set(('test_backup.db', 'test_backup1.db',\n                            'test_backup2.db'))\n\n    def tearDown(self):\n        super(TestBackup, self).tearDown()\n        for backup_filename in self.backup_filenames:\n            if os.path.exists(backup_filename):\n                os.unlink(backup_filename)\n\n    def _populate_test_data(self, nrows=100, db=None):\n        db = self.database if db is None else db\n        db.execute_sql('CREATE TABLE register (id INTEGER NOT NULL PRIMARY KEY'\n                       ', value INTEGER NOT NULL)')\n        with db.atomic():\n            for i in range(nrows):\n                db.execute_sql('INSERT INTO register (value) VALUES (?)', (i,))\n\n    def test_backup(self):\n        self._populate_test_data()\n\n        # Back-up to an in-memory database and verify contents.\n        other_db = CySqliteDatabase(':memory:')\n        self.database.backup(other_db)\n        cursor = other_db.execute_sql('SELECT value FROM register ORDER BY '\n                                      'value;')\n        self.assertEqual([val for val, in cursor.fetchall()], list(range(100)))\n        other_db.close()\n\n    def test_backup_preserve_pagesize(self):\n        db1 = CySqliteDatabase('test_backup1.db')\n        with db1.connection_context():\n            db1.page_size = 8192\n            self._populate_test_data(db=db1)\n\n        db1.connect()\n        self.assertEqual(db1.page_size, 8192)\n\n        db2 = CySqliteDatabase('test_backup2.db')\n        db1.backup(db2)\n        self.assertEqual(db2.page_size, 8192)\n        nrows, = db2.execute_sql('select count(*) from register;').fetchone()\n        self.assertEqual(nrows, 100)\n\n    def test_backup_to_file(self):\n        self._populate_test_data()\n\n        self.database.backup_to_file('test_backup.db')\n        backup_db = CySqliteDatabase('test_backup.db')\n        cursor = backup_db.execute_sql('SELECT value FROM register ORDER BY '\n                                       'value;')\n        self.assertEqual([val for val, in cursor.fetchall()], list(range(100)))\n        backup_db.close()\n\n    def test_backup_progress(self):\n        self._populate_test_data()\n\n        accum = []\n        def progress(remaining, total, is_done):\n            accum.append((remaining, total, is_done))\n\n        other_db = CySqliteDatabase(':memory:')\n        self.database.backup(other_db, pages=1, progress=progress)\n        self.assertTrue(len(accum) > 0)\n\n        sql = 'select value from register order by value;'\n        self.assertEqual([r for r, in other_db.execute_sql(sql)],\n                         list(range(100)))\n        other_db.close()\n\n    def test_backup_progress_error(self):\n        self._populate_test_data()\n\n        def broken_progress(remaining, total, is_done):\n            raise ValueError('broken')\n\n        other_db = CySqliteDatabase(':memory:')\n        self.assertRaises(ValueError, self.database.backup, other_db,\n                          progress=broken_progress)\n        other_db.close()\n\n\nclass DataTypes(cysqlite.TableFunction):\n    columns = ('key', 'value')\n    params = ()\n    name = 'data_types'\n\n    def initialize(self):\n        self.values = (\n            None,\n            1,\n            2.,\n            u'unicode str',\n            b'byte str',\n            False,\n            True)\n        self.idx = 0\n        self.n = len(self.values)\n\n    def iterate(self, idx):\n        if idx < self.n:\n            return ('k%s' % idx, self.values[idx])\n        raise StopIteration\n\n\n@skip_unless(cysqlite.sqlite_version_info >= (3, 9), 'requires sqlite >= 3.9')\nclass TestDataTypesTableFunction(CyDatabaseTestCase):\n    database = db_loader('cysqlite')\n\n    def test_data_types_table_function(self):\n        self.database.register_table_function(DataTypes)\n        for _ in range(2):\n            cursor = self.database.execute_sql('SELECT key, value FROM '\n                                               'data_types() ORDER BY key')\n            self.assertEqual(cursor.fetchall(), [\n                ('k0', None),\n                ('k1', 1),\n                ('k2', 2.),\n                ('k3', u'unicode str'),\n                ('k4', b'byte str'),\n                ('k5', 0),\n                ('k6', 1),\n            ])\n\n            # Ensure table re-registered after close.\n            self.database.close()\n            self.database.connect()\n"
  },
  {
    "path": "tests/dataset.py",
    "content": "import csv\nimport datetime\nimport json\nimport operator\nimport os\nimport tempfile\nfrom io import StringIO\n\nfrom peewee import *\nfrom playhouse.dataset import DataSet\nfrom playhouse.dataset import Table\n\nfrom .base import IS_SQLITE_OLD\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import get_sqlite_db\nfrom .base import requires_models\nfrom .base import skip_if\n\n\ndb = get_sqlite_db()\n\n\nclass User(TestModel):\n    username = TextField(primary_key=True)\n\nclass Note(TestModel):\n    user = ForeignKeyField(User)\n    content = TextField()\n    timestamp = DateTimeField()\n    status = IntegerField(default=1)\n    data = BlobField()\n\nclass Category(TestModel):\n    name = TextField()\n    parent = ForeignKeyField('self', null=True)\n\nclass Bin(TestModel):\n    data = BlobField()\n    ts = DateTimeField()\n\n\nclass TestDataSet(ModelTestCase):\n    database = db\n    requires = [User, Note, Category]\n    names = ['charlie', 'huey', 'peewee', 'mickey', 'zaizee']\n\n    def setUp(self):\n        if os.path.exists(self.database.database):\n            os.unlink(self.database.database)\n        super(TestDataSet, self).setUp()\n\n        self.dataset = DataSet('sqlite:///%s' % self.database.database)\n\n    def tearDown(self):\n        self.dataset.close()\n        super(TestDataSet, self).tearDown()\n\n    def test_create_index(self):\n        users = self.dataset['users']\n        users.insert(username='u0')\n\n        users.create_index(['username'], True)\n        with self.assertRaises(IntegrityError):\n            users.insert(username='u0')\n\n    def test_pass_database(self):\n        db = SqliteDatabase(':memory:')\n        dataset = DataSet(db)\n        self.assertEqual(dataset._database_path, ':memory:')\n\n        users = dataset['users']\n        users.insert(username='charlie')\n        self.assertEqual(list(users), [{'id': 1, 'username': 'charlie'}])\n\n    @skip_if(IS_SQLITE_OLD)\n    def test_with_views(self):\n        self.dataset.query('CREATE VIEW notes_public AS '\n                           'SELECT content, timestamp FROM note '\n                           'WHERE status = 1 ORDER BY timestamp DESC')\n        try:\n            self.assertTrue('notes_public' in self.dataset.views)\n            self.assertFalse('notes_public' in self.dataset.tables)\n\n            users = self.dataset['user']\n            with self.dataset.transaction():\n                users.insert(username='charlie')\n                users.insert(username='huey')\n\n            notes = self.dataset['note']\n            for i, (ct, st) in enumerate([('n1', 1), ('n2', 2), ('n3', 1)]):\n                notes.insert(content=ct, status=st, user_id='charlie',\n                             timestamp=datetime.datetime(2022, 1, 1 + i),\n                             data=b'')\n\n            self.assertFalse('notes_public' in self.dataset)\n\n            # Create a new dataset instance with views enabled.\n            dataset = DataSet(self.dataset._database, include_views=True)\n            self.assertTrue('notes_public' in dataset)\n            public = dataset['notes_public']\n            self.assertEqual(public.columns, ['content', 'timestamp'])\n            self.assertEqual(list(public), [\n                {'content': 'n3', 'timestamp': datetime.datetime(2022, 1, 3)},\n                {'content': 'n1', 'timestamp': datetime.datetime(2022, 1, 1)}])\n        finally:\n            self.dataset.query('DROP VIEW notes_public')\n\n    def test_item_apis(self):\n        dataset = DataSet('sqlite:///:memory:')\n        users = dataset['users']\n        users.insert(username='charlie')\n        self.assertEqual(list(users), [{'id': 1, 'username': 'charlie'}])\n\n        users[2] = {'username': 'huey', 'color': 'white'}\n        self.assertEqual(list(users), [\n            {'id': 1, 'username': 'charlie', 'color': None},\n            {'id': 2, 'username': 'huey', 'color': 'white'}])\n\n        users[2] = {'username': 'huey-x', 'kind': 'cat'}\n        self.assertEqual(list(users), [\n            {'id': 1, 'username': 'charlie', 'color': None, 'kind': None},\n            {'id': 2, 'username': 'huey-x', 'color': 'white', 'kind': 'cat'}])\n\n        del users[2]\n        self.assertEqual(list(users), [\n            {'id': 1, 'username': 'charlie', 'color': None, 'kind': None}])\n\n        users[1] = {'kind': 'person'}\n        users[2] = {'username': 'zaizee'}\n        users[2] = {'kind': 'cat'}\n        self.assertEqual(list(users), [\n            {'id': 1, 'username': 'charlie', 'color': None, 'kind': 'person'},\n            {'id': 2, 'username': 'zaizee', 'color': None, 'kind': 'cat'}])\n\n    def create_users(self, n=2):\n        user = self.dataset['user']\n        for i in range(min(n, len(self.names))):\n            user.insert(username=self.names[i])\n\n    def test_special_char_table(self):\n        self.database.execute_sql('CREATE TABLE \"hello!!world\" (\"data\" TEXT);')\n        self.database.execute_sql('INSERT INTO \"hello!!world\" VALUES (?)',\n                                  ('test',))\n        ds = DataSet('sqlite:///%s' % self.database.database)\n        table = ds['hello!!world']\n        model = table.model_class\n        self.assertEqual(model._meta.table_name, 'hello!!world')\n\n    def test_column_preservation(self):\n        ds = DataSet('sqlite:///:memory:')\n        books = ds['books']\n        books.insert(book_id='BOOK1')\n        books.insert(bookId='BOOK2')\n        data = [(row['book_id'] or '', row['bookId'] or '') for row in books]\n        self.assertEqual(sorted(data), [\n            ('', 'BOOK2'),\n            ('BOOK1', '')])\n\n    def test_case_insensitive(self):\n        db.execute_sql('CREATE TABLE \"SomeTable\" (data TEXT);')\n        tables = sorted(self.dataset.tables)\n        self.assertEqual(tables, ['SomeTable', 'category', 'note', 'user'])\n\n        table = self.dataset['HueyMickey']\n        self.assertEqual(table.model_class._meta.table_name, 'HueyMickey')\n        tables = sorted(self.dataset.tables)\n        self.assertEqual(\n            tables,\n            ['HueyMickey', 'SomeTable', 'category', 'note', 'user'])\n\n        # Subsequent lookup succeeds.\n        self.dataset['HueyMickey']\n\n    def test_introspect(self):\n        tables = sorted(self.dataset.tables)\n        self.assertEqual(tables, ['category', 'note', 'user'])\n\n        user = self.dataset['user']\n        columns = sorted(user.columns)\n        self.assertEqual(columns, ['username'])\n\n        note = self.dataset['note']\n        columns = sorted(note.columns)\n        self.assertEqual(columns, ['content', 'data', 'id', 'status',\n                                   'timestamp', 'user_id'])\n\n        category = self.dataset['category']\n        columns = sorted(category.columns)\n        self.assertEqual(columns, ['id', 'name', 'parent_id'])\n\n    def test_update_cache(self):\n        self.assertEqual(sorted(self.dataset.tables),\n                         ['category', 'note', 'user'])\n\n        db.execute_sql('create table \"foo\" (id INTEGER, data TEXT)')\n        Foo = self.dataset['foo']\n        self.assertEqual(sorted(Foo.columns), ['data', 'id'])\n        self.assertTrue('foo' in self.dataset._models)\n\n        self.dataset._models['foo'].drop_table()\n        self.dataset.update_cache()\n        self.assertTrue('foo' not in self.database.get_tables())\n\n        # This will create the table again.\n        Foo = self.dataset['foo']\n        self.assertTrue('foo' in self.database.get_tables())\n        self.assertEqual(Foo.columns, ['id'])\n\n    def assertQuery(self, query, expected, sort_key='id'):\n        key = operator.itemgetter(sort_key)\n        self.assertEqual(\n            sorted(list(query), key=key),\n            sorted(expected, key=key))\n\n    def test_insert(self):\n        self.create_users()\n        user = self.dataset['user']\n\n        expected = [\n            {'username': 'charlie'},\n            {'username': 'huey'}]\n        self.assertQuery(user.all(), expected, 'username')\n\n        user.insert(username='mickey', age=5)\n        expected = [\n            {'username': 'charlie', 'age': None},\n            {'username': 'huey', 'age': None},\n            {'username': 'mickey', 'age': 5}]\n        self.assertQuery(user.all(), expected, 'username')\n\n        query = user.find(username='charlie')\n        expected = [{'username': 'charlie', 'age': None}]\n        self.assertQuery(query, expected, 'username')\n\n        self.assertEqual(\n            user.find_one(username='mickey'),\n            {'username': 'mickey', 'age': 5})\n\n        self.assertTrue(user.find_one(username='xx') is None)\n\n    def test_update(self):\n        self.create_users()\n        user = self.dataset['user']\n\n        self.assertEqual(user.update(favorite_color='green'), 2)\n        expected = [\n            {'username': 'charlie', 'favorite_color': 'green'},\n            {'username': 'huey', 'favorite_color': 'green'}]\n        self.assertQuery(user.all(), expected, 'username')\n\n        res = user.update(\n            favorite_color='blue',\n            username='huey',\n            columns=['username'])\n        self.assertEqual(res, 1)\n        expected[1]['favorite_color'] = 'blue'\n        self.assertQuery(user.all(), expected, 'username')\n\n    def test_delete(self):\n        self.create_users()\n        user = self.dataset['user']\n        self.assertEqual(user.delete(username='huey'), 1)\n        self.assertEqual(list(user.all()), [{'username': 'charlie'}])\n\n    def test_find(self):\n        self.create_users(5)\n        user = self.dataset['user']\n\n        def assertUsernames(query, expected):\n            self.assertEqual(\n                sorted(row['username'] for row in query),\n                sorted(expected))\n\n        assertUsernames(user.all(), self.names)\n        assertUsernames(user.find(), self.names)\n        assertUsernames(user.find(username='charlie'), ['charlie'])\n        assertUsernames(user.find(username='missing'), [])\n\n        user.update(favorite_color='green')\n        for username in ['zaizee', 'huey']:\n            user.update(\n                favorite_color='blue',\n                username=username,\n                columns=['username'])\n\n        assertUsernames(\n            user.find(favorite_color='green'),\n            ['charlie', 'mickey', 'peewee'])\n        assertUsernames(\n            user.find(favorite_color='blue'),\n            ['zaizee', 'huey'])\n        assertUsernames(\n            user.find(favorite_color='green', username='peewee'),\n            ['peewee'])\n\n        self.assertEqual(\n            user.find_one(username='charlie'),\n            {'username': 'charlie', 'favorite_color': 'green'})\n\n    def test_magic_methods(self):\n        self.create_users(5)\n        user = self.dataset['user']\n\n        # __len__()\n        self.assertEqual(len(user), 5)\n\n        # __iter__()\n        users = sorted([u for u in user], key=operator.itemgetter('username'))\n        self.assertEqual(users[0], {'username': 'charlie'})\n        self.assertEqual(users[-1], {'username': 'zaizee'})\n\n        # __contains__()\n        self.assertTrue('user' in self.dataset)\n        self.assertFalse('missing' in self.dataset)\n\n    def test_foreign_keys(self):\n        user = self.dataset['user']\n        user.insert(username='charlie')\n\n        note = self.dataset['note']\n        for i in range(1, 4):\n            note.insert(\n                content='note %s' % i,\n                timestamp=datetime.date(2014, 1, i),\n                status=i,\n                user_id='charlie',\n                data=b'')\n\n        notes = sorted(note.all(), key=operator.itemgetter('id'))\n        self.assertEqual(notes[0], {\n            'content': 'note 1',\n            'data': b'',\n            'id': 1,\n            'status': 1,\n            'timestamp': datetime.datetime(2014, 1, 1),\n            'user_id': 'charlie'})\n        self.assertEqual(notes[-1], {\n            'content': 'note 3',\n            'data': b'',\n            'id': 3,\n            'status': 3,\n            'timestamp': datetime.datetime(2014, 1, 3),\n            'user_id': 'charlie'})\n\n        user.insert(username='mickey')\n        note.update(user_id='mickey', id=3, columns=['id'])\n\n        self.assertEqual(note.find(user_id='charlie').count(), 2)\n        self.assertEqual(note.find(user_id='mickey').count(), 1)\n\n        category = self.dataset['category']\n        category.insert(name='c1')\n        c1 = category.find_one(name='c1')\n        self.assertEqual(c1, {'id': 1, 'name': 'c1', 'parent_id': None})\n\n        category.insert(name='c2', parent_id=1)\n        c2 = category.find_one(parent_id=1)\n        self.assertEqual(c2, {'id': 2, 'name': 'c2', 'parent_id': 1})\n\n        self.assertEqual(category.delete(parent_id=1), 1)\n        self.assertEqual(list(category.all()), [c1])\n\n    def test_transactions(self):\n        user = self.dataset['user']\n        with self.dataset.transaction() as txn:\n            user.insert(username='u1')\n            with self.dataset.transaction() as txn2:\n                user.insert(username='u2')\n                txn2.rollback()\n\n            with self.dataset.transaction() as txn3:\n                user.insert(username='u3')\n                with self.dataset.transaction() as txn4:\n                    user.insert(username='u4')\n                txn3.rollback()\n\n            with self.dataset.transaction() as txn5:\n                user.insert(username='u5')\n                with self.dataset.transaction() as txn6:\n                    with self.dataset.transaction() as txn7:\n                        user.insert(username='u6')\n                        txn7.rollback()\n                    user.insert(username='u7')\n\n            user.insert(username='u8')\n\n        self.assertQuery(user.all(), [\n            {'username': 'u1'},\n            {'username': 'u5'},\n            {'username': 'u7'},\n            {'username': 'u8'},\n        ], 'username')\n\n    def test_export(self):\n        self.create_users()\n        user = self.dataset['user']\n\n        buf = StringIO()\n        self.dataset.freeze(user.all(), 'json', file_obj=buf)\n        self.assertEqual(buf.getvalue(), (\n            '[{\"username\": \"charlie\"}, {\"username\": \"huey\"}]'))\n\n        buf = StringIO()\n        self.dataset.freeze(user.all(), 'csv', file_obj=buf)\n        self.assertEqual(buf.getvalue().splitlines(), [\n            'username',\n            'charlie',\n            'huey'])\n\n    def test_freeze_thaw_csv_utf8(self):\n        self._test_freeze_thaw_utf8('csv')\n\n    def test_freeze_thaw_json_utf8(self):\n        self._test_freeze_thaw_utf8('json')\n\n    def _test_freeze_thaw_utf8(self, fmt):\n        username_bytes = b'\\xd0\\x92obby'  # Bobby with cyrillic \"B\".\n        username_str = username_bytes.decode('utf8')\n        u = User.create(username=username_str)\n\n        # Freeze the data as a the given format.\n        user = self.dataset['user']\n        filename = tempfile.mktemp()  # Get a filename.\n        self.dataset.freeze(user.all(), fmt, filename)\n\n        # Clear out the table and reload.\n        User.delete().execute()\n        self.assertEqual(list(user.all()), [])\n\n        # Thaw the frozen data.\n        n = user.thaw(format=fmt, filename=filename)\n        self.assertEqual(n, 1)\n        self.assertEqual(list(user.all()), [{'username': username_str}])\n\n    def test_freeze_thaw(self):\n        user = self.dataset['user']\n        user.insert(username='charlie')\n\n        note = self.dataset['note']\n        note_ts = datetime.datetime(2017, 1, 2, 3, 4, 5)\n        note.insert(content='foo', timestamp=note_ts, user_id='charlie',\n                    status=2, data=b'\\xff\\x00\\xcc')\n\n        buf = StringIO()\n        self.dataset.freeze(note.all(), 'json', file_obj=buf)\n        self.assertEqual(json.loads(buf.getvalue()), [{\n            'id': 1,\n            'user_id': 'charlie',\n            'content': 'foo',\n            'status': 2,\n            'timestamp': '2017-01-02 03:04:05',\n            'data': 'ff00cc'}])\n\n        note.delete(id=1)\n        self.assertEqual(list(note.all()), [])\n\n        buf.seek(0)\n        note.thaw(format='json', file_obj=buf)\n        self.assertEqual(list(note.all()), [{\n            'id': 1,\n            'user_id': 'charlie',\n            'content': 'foo',\n            'status': 2,\n            'timestamp': note_ts,\n            'data': b'\\xff\\x00\\xcc'}])\n\n    @requires_models(Bin)\n    def test_freeze_thaw_datatypes_json(self):\n        Bin = self.dataset['bin']\n        ts = datetime.datetime(2026, 1, 2, 3, 4, 5,\n                               tzinfo=datetime.timezone.utc)\n\n        Bin.insert(data=b'\\xff\\x00\\xcc', ts=ts)\n\n        buf = StringIO()\n        self.dataset.freeze(Bin.all(), 'json', file_obj=buf)\n        self.assertEqual(json.loads(buf.getvalue()), [{\n            'id': 1,\n            'data': 'ff00cc',\n            'ts': '2026-01-02 03:04:05+00:00'}])\n\n        Bin.delete(id=1)\n        buf.seek(0)\n        Bin.thaw(format='json', file_obj=buf)\n\n        self.assertEqual(list(Bin.all()), [{\n            'id': 1,\n            'data': b'\\xff\\x00\\xcc',\n            'ts': ts}])\n\n        buf = StringIO()\n        self.dataset.freeze(Bin.all(), 'json', file_obj=buf,\n                            iso8601_datetimes=True, base64_bytes=True)\n        self.assertEqual(json.loads(buf.getvalue()), [{\n            'id': 1,\n            'data': '_wDM',\n            'ts': '2026-01-02T03:04:05+00:00'}])\n\n        Bin.delete(id=1)\n        buf.seek(0)\n        Bin.thaw(format='json', file_obj=buf, iso8601_datetimes=True,\n                 base64_bytes=True)\n\n        self.assertEqual(list(Bin.all()), [{\n            'id': 1,\n            'data': b'\\xff\\x00\\xcc',\n            'ts': ts}])\n\n    @requires_models(Bin)\n    def test_freeze_thaw_datatypes_csv(self):\n        Bin = self.dataset['bin']\n        ts = datetime.datetime(2026, 1, 2, 3, 4, 5,\n                               tzinfo=datetime.timezone.utc)\n\n        Bin.insert(data=b'\\xff\\x00\\xcc', ts=ts)\n\n        buf = StringIO()\n        self.dataset.freeze(Bin.all(), 'csv', file_obj=buf)\n        self.assertEqual(buf.getvalue().splitlines(), [\n            'id,data,ts',\n            '1,ff00cc,2026-01-02 03:04:05+00:00'])\n\n        Bin.delete(id=1)\n        buf.seek(0)\n        Bin.thaw(format='csv', file_obj=buf)\n\n        self.assertEqual(list(Bin.all()), [{\n            'id': 1,\n            'data': b'\\xff\\x00\\xcc',\n            'ts': ts}])\n\n        buf = StringIO()\n        self.dataset.freeze(Bin.all(), 'csv', file_obj=buf,\n                            iso8601_datetimes=True, base64_bytes=True)\n        self.assertEqual(buf.getvalue().splitlines(), [\n            'id,data,ts',\n            '1,_wDM,2026-01-02T03:04:05+00:00'])\n\n        Bin.delete(id=1)\n        buf.seek(0)\n        Bin.thaw(format='csv', file_obj=buf, iso8601_datetimes=True,\n                 base64_bytes=True)\n\n        self.assertEqual(list(Bin.all()), [{\n            'id': 1,\n            'data': b'\\xff\\x00\\xcc',\n            'ts': ts}])\n\n    def test_table_column_creation(self):\n        table = self.dataset['people']\n        table.insert(name='charlie')\n        self.assertEqual(table.columns, ['id', 'name'])\n        self.assertEqual(list(table.all()), [{'id': 1, 'name': 'charlie'}])\n\n    def test_table_column_creation_field_col(self):\n        table = self.dataset['people']\n        table.insert(**{'First Name': 'charlie'})\n        self.assertEqual(table.columns, ['id', 'First_Name'])\n        self.assertEqual(list(table.all()), [{'id': 1, 'First_Name': 'charlie'}])\n\n        table.insert(**{'First Name': 'huey'})\n        self.assertEqual(table.columns, ['id', 'First_Name'])\n        self.assertEqual(list(table.all().order_by(table.model_class.id)), [\n            {'id': 1, 'First_Name': 'charlie'},\n            {'id': 2, 'First_Name': 'huey'}])\n\n    def test_import_json(self):\n        table = self.dataset['people']\n        table.insert(name='charlie')\n\n        data = [\n            {'name': 'zaizee', 'foo': 1},\n            {'name': 'huey'},\n            {'name': 'mickey', 'foo': 2},\n            {'bar': None}]\n        buf = StringIO()\n        json.dump(data, buf)\n        buf.seek(0)\n\n        # All rows but the last will be inserted.\n        count = self.dataset.thaw('people', 'json', file_obj=buf, strict=True)\n        self.assertEqual(count, 3)\n\n        names = [row['name'] for row in self.dataset['people'].all()]\n        self.assertEqual(\n            set(names),\n            set(['charlie', 'huey', 'mickey', 'zaizee']))\n\n        # The columns have not changed.\n        self.assertEqual(table.columns, ['id', 'name'])\n\n        # No rows are inserted because no column overlap between `user` and the\n        # provided data.\n        buf.seek(0)\n        count = self.dataset.thaw('user', 'json', file_obj=buf, strict=True)\n        self.assertEqual(count, 0)\n\n        # Create a new table and load all data into it.\n        table = self.dataset['more_people']\n\n        # All rows and columns will be inserted.\n        buf.seek(0)\n        count = self.dataset.thaw('more_people', 'json', file_obj=buf)\n        self.assertEqual(count, 4)\n\n        self.assertEqual(\n            set(table.columns),\n            set(['id', 'name', 'bar', 'foo']))\n        self.assertEqual(sorted(table.all(), key=lambda row: row['id']), [\n            {'id': 1, 'name': 'zaizee', 'foo': 1, 'bar': None},\n            {'id': 2, 'name': 'huey', 'foo': None, 'bar': None},\n            {'id': 3, 'name': 'mickey', 'foo': 2, 'bar': None},\n            {'id': 4, 'name': None, 'foo': None, 'bar': None},\n        ])\n\n    def test_import_csv(self):\n        table = self.dataset['people']\n        table.insert(name='charlie')\n\n        data = [\n            ('zaizee', 1, None),\n            ('huey', 2, 'foo'),\n            ('mickey', 3, 'baze')]\n        buf = StringIO()\n        writer = csv.writer(buf)\n        writer.writerow(['name', 'foo', 'bar'])\n        writer.writerows(data)\n\n        buf.seek(0)\n        count = self.dataset.thaw('people', 'csv', file_obj=buf, strict=True)\n        self.assertEqual(count, 3)\n\n        names = [row['name'] for row in self.dataset['people'].all()]\n        self.assertEqual(\n            set(names),\n            set(['charlie', 'huey', 'mickey', 'zaizee']))\n\n        # The columns have not changed.\n        self.assertEqual(table.columns, ['id', 'name'])\n\n        # No rows are inserted because no column overlap between `user` and the\n        # provided data.\n        buf.seek(0)\n        count = self.dataset.thaw('user', 'csv', file_obj=buf, strict=True)\n        self.assertEqual(count, 0)\n\n        # Create a new table and load all data into it.\n        table = self.dataset['more_people']\n\n        # All rows and columns will be inserted.\n        buf.seek(0)\n        count = self.dataset.thaw('more_people', 'csv', file_obj=buf)\n        self.assertEqual(count, 3)\n\n        self.assertEqual(\n            set(table.columns),\n            set(['id', 'name', 'bar', 'foo']))\n        self.assertEqual(sorted(table.all(), key=lambda row: row['id']), [\n            {'id': 1, 'name': 'zaizee', 'foo': '1', 'bar': ''},\n            {'id': 2, 'name': 'huey', 'foo': '2', 'bar': 'foo'},\n            {'id': 3, 'name': 'mickey', 'foo': '3', 'bar': 'baze'},\n        ])\n\n    def test_table_thaw(self):\n        table = self.dataset['people']\n        data = json.dumps([{'name': 'charlie'}, {'name': 'huey', 'color': 'white'}])\n        self.assertEqual(table.thaw(file_obj=StringIO(data), format='json'), 2)\n        self.assertEqual(list(table.all()), [\n            {'id': 1, 'name': 'charlie', 'color': None},\n            {'id': 2, 'name': 'huey', 'color': 'white'},\n        ])\n\n    def test_creating_tables(self):\n        new_table = self.dataset['new_table']\n        new_table.insert(data='foo')\n\n        ref2 = self.dataset['new_table']\n        self.assertEqual(list(ref2.all()), [{'id': 1, 'data': 'foo'}])\n"
  },
  {
    "path": "tests/db_tests.py",
    "content": "from itertools import permutations\nfrom queue import Queue\nimport platform\nimport re\nimport threading\n\nfrom peewee import *\nfrom peewee import Database\nfrom peewee import FIELD\nfrom peewee import attrdict\nfrom peewee import sort_models\n\nfrom .base import BaseTestCase\nfrom .base import DatabaseTestCase\nfrom .base import IS_CRDB\nfrom .base import IS_MYSQL\nfrom .base import IS_POSTGRESQL\nfrom .base import IS_SQLITE\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db\nfrom .base import get_in_memory_db\nfrom .base import get_sqlite_db\nfrom .base import new_connection\nfrom .base import requires_models\nfrom .base import requires_postgresql\nfrom .base_models import Category\nfrom .base_models import Tweet\nfrom .base_models import User\n\n\nclass TestDatabase(DatabaseTestCase):\n    database = get_sqlite_db()\n\n    def test_pragmas(self):\n        self.database.cache_size = -2048\n        self.assertEqual(self.database.cache_size, -2048)\n        self.database.cache_size = -4096\n        self.assertEqual(self.database.cache_size, -4096)\n\n        self.database.foreign_keys = 'on'\n        self.assertEqual(self.database.foreign_keys, 1)\n        self.database.foreign_keys = 'off'\n        self.assertEqual(self.database.foreign_keys, 0)\n\n    def test_appid_user_version(self):\n        self.assertEqual(self.database.application_id, 0)\n        self.assertEqual(self.database.user_version, 0)\n        self.database.application_id = 1\n        self.database.user_version = 2\n        self.assertEqual(self.database.application_id, 1)\n        self.assertEqual(self.database.user_version, 2)\n        self.assertTrue(self.database.close())\n        self.assertTrue(self.database.connect())\n        self.assertEqual(self.database.application_id, 1)\n        self.assertEqual(self.database.user_version, 2)\n\n    def test_timeout_semantics(self):\n        self.assertEqual(self.database.timeout, 5)\n        self.assertEqual(self.database.pragma('busy_timeout'), 5000)\n\n        self.database.timeout = 2.5\n        self.assertEqual(self.database.timeout, 2.5)\n        self.assertEqual(self.database.pragma('busy_timeout'), 2500)\n\n        self.database.close()\n        self.database.connect()\n\n        self.assertEqual(self.database.timeout, 2.5)\n        self.assertEqual(self.database.pragma('busy_timeout'), 2500)\n\n    def test_pragmas_deferred(self):\n        pragmas = (('journal_mode', 'wal'),)\n        db = SqliteDatabase(None, pragmas=pragmas)\n        self.assertEqual(db._pragmas, pragmas)\n\n        # Test pragmas preserved after initializing.\n        db.init(':memory:')\n        self.assertEqual(db._pragmas, pragmas)\n\n        db = SqliteDatabase(None)\n        self.assertEqual(db._pragmas, ())\n\n        # Test pragmas are set and subsequently overwritten.\n        db.init(':memory:', pragmas=pragmas)\n        self.assertEqual(db._pragmas, pragmas)\n\n        db.init(':memory:', pragmas=())\n        self.assertEqual(db._pragmas, ())\n\n        # Test when specified twice, the previous value is overwritten.\n        db = SqliteDatabase(None, pragmas=pragmas)\n        db.init(':memory:', pragmas=(('cache_size', -8000),))\n        self.assertEqual(db._pragmas, (('cache_size', -8000),))\n\n    def test_pragmas_as_dict(self):\n        pragmas = {'journal_mode': 'wal'}\n        pragma_list = [('journal_mode', 'wal')]\n\n        db = SqliteDatabase(':memory:', pragmas=pragmas)\n        self.assertEqual(db._pragmas, pragma_list)\n\n        # Test deferred databases correctly handle pragma dicts.\n        db = SqliteDatabase(None, pragmas=pragmas)\n        self.assertEqual(db._pragmas, pragma_list)\n\n        db.init(':memory:')\n        self.assertEqual(db._pragmas, pragma_list)\n\n        db.init(':memory:', pragmas={})\n        self.assertEqual(db._pragmas, [])\n\n    def test_pragmas_permanent(self):\n        db = SqliteDatabase(':memory:')\n        db.execute_sql('pragma foreign_keys=0')\n        self.assertEqual(db.foreign_keys, 0)\n\n        db.pragma('foreign_keys', 1, True)\n        self.assertEqual(db.foreign_keys, 1)\n\n        db.close()\n        db.connect()\n        self.assertEqual(db.foreign_keys, 1)\n\n    def test_context_settings(self):\n        class TestDatabase(Database):\n            field_types = {'BIGINT': 'TEST_BIGINT', 'TEXT': 'TEST_TEXT'}\n            operations = {'LIKE': '~', 'NEW': '->>'}\n            param = '$'\n\n        test_db = TestDatabase(None)\n        state = test_db.get_sql_context().state\n\n        self.assertEqual(state.field_types['BIGINT'], 'TEST_BIGINT')\n        self.assertEqual(state.field_types['TEXT'], 'TEST_TEXT')\n        self.assertEqual(state.field_types['INT'], FIELD.INT)\n        self.assertEqual(state.field_types['VARCHAR'], FIELD.VARCHAR)\n\n        self.assertEqual(state.operations['LIKE'], '~')\n        self.assertEqual(state.operations['NEW'], '->>')\n        self.assertEqual(state.operations['ILIKE'], 'ILIKE')\n\n        self.assertEqual(state.param, '$')\n        self.assertEqual(state.quote, '\"\"')\n\n        test_db2 = TestDatabase(None, field_types={'BIGINT': 'XXX_BIGINT',\n                                                   'INT': 'XXX_INT'})\n        state = test_db2.get_sql_context().state\n        self.assertEqual(state.field_types['BIGINT'], 'XXX_BIGINT')\n        self.assertEqual(state.field_types['TEXT'], 'TEST_TEXT')\n        self.assertEqual(state.field_types['INT'], 'XXX_INT')\n        self.assertEqual(state.field_types['VARCHAR'], FIELD.VARCHAR)\n\n    def test_connection_state(self):\n        conn = self.database.connection()\n        self.assertFalse(self.database.is_closed())\n        self.database.close()\n        self.assertTrue(self.database.is_closed())\n        conn = self.database.connection()\n        self.assertFalse(self.database.is_closed())\n\n    def test_db_context_manager(self):\n        self.database.close()\n        self.assertTrue(self.database.is_closed())\n\n        with self.database:\n            self.assertFalse(self.database.is_closed())\n\n        self.assertTrue(self.database.is_closed())\n        self.database.connect()\n        self.assertFalse(self.database.is_closed())\n\n        # Enter context with an already-open db.\n        with self.database:\n            self.assertFalse(self.database.is_closed())\n\n        # Closed after exit.\n        self.assertTrue(self.database.is_closed())\n\n    def test_connection_initialization(self):\n        state = {'count': 0}\n        class TestDatabase(SqliteDatabase):\n            def _initialize_connection(self, conn):\n                state['count'] += 1\n        db = TestDatabase(':memory:')\n        self.assertEqual(state['count'], 0)\n\n        conn = db.connection()\n        self.assertEqual(state['count'], 1)\n\n        # Since already connected, nothing happens here.\n        conn = db.connection()\n        self.assertEqual(state['count'], 1)\n\n    def test_connect_semantics(self):\n        state = {'count': 0}\n        class TestDatabase(SqliteDatabase):\n            def _initialize_connection(self, conn):\n                state['count'] += 1\n        db = TestDatabase(':memory:')\n\n        db.connect()\n        self.assertEqual(state['count'], 1)\n        self.assertRaises(OperationalError, db.connect)\n        self.assertEqual(state['count'], 1)\n\n        self.assertFalse(db.connect(reuse_if_open=True))\n        self.assertEqual(state['count'], 1)\n\n        with db:\n            self.assertEqual(state['count'], 1)\n            self.assertFalse(db.is_closed())\n\n        self.assertTrue(db.is_closed())\n        with db:\n            self.assertEqual(state['count'], 2)\n\n    def test_execute_sql(self):\n        self.database.execute_sql('CREATE TABLE register (val INTEGER);')\n        self.database.execute_sql('INSERT INTO register (val) VALUES (?), (?)',\n                                  (1337, 31337))\n        cursor = self.database.execute_sql(\n            'SELECT val FROM register ORDER BY val')\n        self.assertEqual(cursor.fetchall(), [(1337,), (31337,)])\n        self.database.execute_sql('DROP TABLE register;')\n\n    def test_bind_helpers(self):\n        db = get_in_memory_db()\n        alt_db = get_in_memory_db()\n\n        class Base(Model):\n            class Meta:\n                database = db\n\n        class A(Base):\n            a = TextField()\n        class B(Base):\n            b = TextField()\n\n        db.create_tables([A, B])\n\n        # Temporarily bind A to alt_db.\n        with alt_db.bind_ctx([A]):\n            self.assertFalse(A.table_exists())\n            self.assertTrue(B.table_exists())\n\n        self.assertTrue(A.table_exists())\n        self.assertTrue(B.table_exists())\n\n        alt_db.bind([A])\n        self.assertFalse(A.table_exists())\n        self.assertTrue(B.table_exists())\n        db.close()\n        alt_db.close()\n\n    def test_bind_regression(self):\n        class Base(Model):\n            class Meta:\n                database = None\n\n        class A(Base): pass\n        class B(Base): pass\n        class AB(Base):\n            a = ForeignKeyField(A)\n            b = ForeignKeyField(B)\n\n        self.assertTrue(A._meta.database is None)\n\n        db = get_in_memory_db()\n        with db.bind_ctx([A, B]):\n            self.assertEqual(A._meta.database, db)\n            self.assertEqual(B._meta.database, db)\n            self.assertEqual(AB._meta.database, db)\n\n        self.assertTrue(A._meta.database is None)\n        self.assertTrue(B._meta.database is None)\n        self.assertTrue(AB._meta.database is None)\n\n        class C(Base):\n            a = ForeignKeyField(A)\n\n        with db.bind_ctx([C], bind_refs=False):\n            self.assertEqual(C._meta.database, db)\n            self.assertTrue(A._meta.database is None)\n\n        self.assertTrue(C._meta.database is None)\n        self.assertTrue(A._meta.database is None)\n\n    def test_batch_commit(self):\n        class PatchCommitDatabase(SqliteDatabase):\n            commits = 0\n            def begin(self): pass\n            def commit(self):\n                self.commits += 1\n\n        db = PatchCommitDatabase(':memory:')\n\n        def assertBatches(n_objs, batch_size, n_commits):\n            accum = []\n            source = range(n_objs)\n            db.commits = 0\n            for item in db.batch_commit(source, batch_size):\n                accum.append(item)\n\n            self.assertEqual(accum, list(range(n_objs)))\n            self.assertEqual(db.commits, n_commits)\n\n        assertBatches(12, 1, 12)\n        assertBatches(12, 2, 6)\n        assertBatches(12, 3, 4)\n        assertBatches(12, 4, 3)\n        assertBatches(12, 5, 3)\n        assertBatches(12, 6, 2)\n        assertBatches(12, 7, 2)\n        assertBatches(12, 11, 2)\n        assertBatches(12, 12, 1)\n        assertBatches(12, 13, 1)\n\n    def test_server_version(self):\n        class FakeDatabase(Database):\n            server_version = None\n            def _connect(self):\n                return 1\n            def _close(self, conn):\n                pass\n            def _set_server_version(self, conn):\n                self.server_version = (1, 33, 7)\n\n        db = FakeDatabase(':memory:')\n        self.assertTrue(db.server_version is None)\n        db.connect()\n        self.assertEqual(db.server_version, (1, 33, 7))\n        db.close()\n        self.assertEqual(db.server_version, (1, 33, 7))\n\n        db.server_version = (1, 2, 3)\n        db.connect()\n        self.assertEqual(db.server_version, (1, 2, 3))\n        db.close()\n\n    def test_explicit_connect(self):\n        db = get_in_memory_db(autoconnect=False)\n        self.assertRaises(InterfaceError, db.execute_sql, 'pragma cache_size')\n        with db:\n            db.execute_sql('pragma cache_size')\n        self.assertRaises(InterfaceError, db.cursor)\n\n\nclass TestThreadSafety(ModelTestCase):\n    # HACK: This workaround increases the Sqlite busy timeout when tests are\n    # being run on certain architectures.\n    if IS_SQLITE and platform.machine() not in ('i386', 'i686', 'x86_64'):\n        database = new_connection(timeout=60)\n    nthreads = 4\n    nrows = 10\n    requires = [User]\n\n    def test_multiple_writers(self):\n        def create_users(idx):\n            for i in range(idx * self.nrows, (idx + 1) * self.nrows):\n                User.create(username='u%d' % i)\n\n        threads = []\n        for i in range(self.nthreads):\n            threads.append(threading.Thread(target=create_users, args=(i,)))\n\n        for t in threads: t.start()\n        for t in threads: t.join()\n\n        self.assertEqual(User.select().count(), self.nrows * self.nthreads)\n\n    def test_multiple_readers(self):\n        data = Queue()\n        def read_user_count(n):\n            for i in range(n):\n                data.put(User.select().count())\n\n        threads = []\n        for i in range(self.nthreads):\n            threads.append(threading.Thread(target=read_user_count,\n                                            args=(self.nrows,)))\n\n        for t in threads: t.start()\n        for t in threads: t.join()\n        self.assertEqual(data.qsize(), self.nrows * self.nthreads)\n\n    def test_mt_general(self):\n        def connect_close():\n            for _ in range(self.nrows):\n                self.database.connect()\n                with self.database.atomic() as txn:\n                    self.database.execute_sql('select 1').fetchone()\n                self.database.close()\n\n        threads = []\n        for i in range(self.nthreads):\n            threads.append(threading.Thread(target=connect_close))\n\n        for t in threads: t.start()\n        for t in threads: t.join()\n\n\nclass TestDeferredDatabase(BaseTestCase):\n    def test_deferred_database(self):\n        deferred_db = SqliteDatabase(None)\n        self.assertTrue(deferred_db.deferred)\n\n        class DeferredModel(Model):\n            class Meta:\n                database = deferred_db\n\n        self.assertRaises(Exception, deferred_db.connect)\n        query = DeferredModel.select()\n        self.assertRaises(Exception, query.execute)\n\n        deferred_db.init(':memory:')\n        self.assertFalse(deferred_db.deferred)\n\n        conn = deferred_db.connect()\n        self.assertFalse(deferred_db.is_closed())\n        DeferredModel._schema.create_all()\n        self.assertEqual(list(DeferredModel.select()), [])\n\n        deferred_db.init(None)\n        self.assertTrue(deferred_db.deferred)\n\n        # The connection was automatically closed.\n        self.assertTrue(deferred_db.is_closed())\n\n\nclass CatToy(TestModel):\n    description = TextField()\n\n    class Meta:\n        schema = 'huey'\n\n\n@requires_postgresql\nclass TestSchemaNamespace(ModelTestCase):\n    requires = [CatToy]\n\n    def setUp(self):\n        with self.database:\n            self.execute('CREATE SCHEMA huey;')\n        super(TestSchemaNamespace, self).setUp()\n\n    def tearDown(self):\n        super(TestSchemaNamespace, self).tearDown()\n        with self.database:\n            self.execute('DROP SCHEMA huey;')\n\n    def test_schema(self):\n        toy = CatToy.create(description='fur mouse')\n        toy_db = CatToy.select().where(CatToy.id == toy.id).get()\n        self.assertEqual(toy.id, toy_db.id)\n        self.assertEqual(toy.description, toy_db.description)\n\n\nclass TestSqliteIsolation(ModelTestCase):\n    database = get_sqlite_db()\n    requires = [User]\n\n    def test_sqlite_isolation(self):\n        for username in ('u1', 'u2', 'u3'): User.create(username=username)\n\n        new_db = get_sqlite_db()\n        curs = new_db.execute_sql('SELECT COUNT(*) FROM users')\n        self.assertEqual(curs.fetchone()[0], 3)\n\n        self.assertEqual(User.select().count(), 3)\n        self.assertEqual(User.delete().execute(), 3)\n\n        with self.database.atomic():\n            User.create(username='u4')\n            User.create(username='u5')\n\n            # Second conn does not see the changes.\n            curs = new_db.execute_sql('SELECT COUNT(*) FROM users')\n            self.assertEqual(curs.fetchone()[0], 0)\n\n            # Third conn does not see the changes.\n            new_db2 = get_sqlite_db()\n            curs = new_db2.execute_sql('SELECT COUNT(*) FROM users')\n            self.assertEqual(curs.fetchone()[0], 0)\n\n            # Original connection sees its own changes.\n            self.assertEqual(User.select().count(), 2)\n\n        curs = new_db.execute_sql('SELECT COUNT(*) FROM users')\n        self.assertEqual(curs.fetchone()[0], 2)\n\n\nclass UniqueModel(TestModel):\n    name = CharField(unique=True)\n\n\nclass IndexedModel(TestModel):\n    first = CharField()\n    last = CharField()\n    dob = DateField()\n\n    class Meta:\n        indexes = (\n            (('first', 'last', 'dob'), True),\n            (('first', 'last'), False),\n        )\n\n\nclass Note(TestModel):\n    content = TextField()\n    ts = DateTimeField()\n    status = IntegerField()\n\n    class Meta:\n        table_name = 'notes'\n\n\nclass Person(TestModel):\n    first = CharField()\n    last = CharField()\n    email = CharField()\n    class Meta:\n        indexes = (\n            (('last', 'first'), False),\n        )\n\n\nclass TestIntrospection(ModelTestCase):\n    requires = [Category, User, UniqueModel, IndexedModel, Person]\n\n    def test_table_exists(self):\n        self.assertTrue(self.database.table_exists(User._meta.table_name))\n        self.assertFalse(self.database.table_exists('nuggies'))\n\n        self.assertTrue(self.database.table_exists(User))\n        class X(TestModel): pass\n        self.assertFalse(self.database.table_exists(X))\n\n    def test_get_tables(self):\n        tables = self.database.get_tables()\n        required = set(m._meta.table_name for m in self.requires)\n        self.assertTrue(required.issubset(set(tables)))\n\n        UniqueModel._schema.drop_all()\n        tables = self.database.get_tables()\n        self.assertFalse(UniqueModel._meta.table_name in tables)\n\n    def test_get_indexes(self):\n        indexes = self.database.get_indexes('unique_model')\n        data = [(index.name, index.columns, index.unique, index.table)\n                for index in indexes\n                if index.name not in ('unique_model_pkey', 'PRIMARY')]\n        self.assertEqual(data, [\n            ('unique_model_name', ['name'], True, 'unique_model')])\n\n        indexes = self.database.get_indexes('indexed_model')\n        data = [(index.name, index.columns, index.unique, index.table)\n                for index in indexes\n                if index.name not in ('indexed_model_pkey', 'PRIMARY')]\n        self.assertEqual(sorted(data), [\n            ('indexed_model_first_last', ['first', 'last'], False,\n             'indexed_model'),\n            ('indexed_model_first_last_dob', ['first', 'last', 'dob'], True,\n             'indexed_model')])\n\n        # Multi-column index where columns are in different order than declared\n        # on the table.\n        indexes = self.database.get_indexes('person')\n        data = [(index.name, index.columns, index.unique)\n                for index in indexes\n                if index.name not in ('person_pkey', 'PRIMARY')]\n        self.assertEqual(data, [\n            ('person_last_first', ['last', 'first'], False)])\n\n    def test_get_columns(self):\n        columns = self.database.get_columns('indexed_model')\n        data = [(c.name, c.null, c.primary_key, c.table)\n                for c in columns]\n        self.assertEqual(data, [\n            ('id', False, True, 'indexed_model'),\n            ('first', False, False, 'indexed_model'),\n            ('last', False, False, 'indexed_model'),\n            ('dob', False, False, 'indexed_model')])\n\n        columns = self.database.get_columns('category')\n        data = [(c.name, c.null, c.primary_key, c.table)\n                for c in columns]\n        self.assertEqual(data, [\n            ('name', False, True, 'category'),\n            ('parent_id', True, False, 'category')])\n\n    def test_get_primary_keys(self):\n        primary_keys = self.database.get_primary_keys('users')\n        self.assertEqual(primary_keys, ['id'])\n\n        primary_keys = self.database.get_primary_keys('category')\n        self.assertEqual(primary_keys, ['name'])\n\n    @requires_models(Note)\n    def test_get_views(self):\n        def normalize_view_meta(view_meta):\n            sql_ws_norm = re.sub(r'[\\n\\s]+', ' ', view_meta.sql.strip('; '))\n            return view_meta.name, (sql_ws_norm\n                                    .replace('`peewee_test`.', '')\n                                    .replace('`notes`.', '')\n                                    .replace('notes.', '')\n                                    .replace('`', ''))\n\n        def assertViews(expected):\n            # Create two sample views.\n            self.database.execute_sql('CREATE VIEW notes_public AS '\n                                      'SELECT content, ts FROM notes '\n                                      'WHERE status = 1 ORDER BY ts DESC')\n            self.database.execute_sql('CREATE VIEW notes_deleted AS '\n                                      'SELECT content FROM notes '\n                                      'WHERE status = 9 ORDER BY id DESC')\n            try:\n                views = self.database.get_views()\n                normalized = sorted([normalize_view_meta(v) for v in views])\n                self.assertEqual(normalized, expected)\n\n                # Ensure that we can use get_columns to introspect views.\n                columns = self.database.get_columns('notes_deleted')\n                self.assertEqual([c.name for c in columns], ['content'])\n\n                columns = self.database.get_columns('notes_public')\n                self.assertEqual([c.name for c in columns], ['content', 'ts'])\n            finally:\n                self.database.execute_sql('DROP VIEW notes_public;')\n                self.database.execute_sql('DROP VIEW notes_deleted;')\n\n        # Unfortunately, all databases seem to represent VIEW definitions\n        # differently internally.\n        if IS_SQLITE:\n            assertViews([\n                ('notes_deleted', ('CREATE VIEW notes_deleted AS '\n                                   'SELECT content FROM notes '\n                                   'WHERE status = 9 ORDER BY id DESC')),\n                ('notes_public', ('CREATE VIEW notes_public AS '\n                                  'SELECT content, ts FROM notes '\n                                  'WHERE status = 1 ORDER BY ts DESC'))])\n        elif IS_MYSQL:\n            assertViews([\n                ('notes_deleted',\n                 ('select content AS content from notes '\n                  'where status = 9 order by id desc')),\n                ('notes_public',\n                 ('select content AS content,ts AS ts from notes '\n                  'where status = 1 order by ts desc'))])\n        elif IS_POSTGRESQL:\n            assertViews([\n                ('notes_deleted',\n                 ('SELECT content FROM notes '\n                  'WHERE (status = 9) ORDER BY id DESC')),\n                ('notes_public',\n                 ('SELECT content, ts FROM notes '\n                  'WHERE (status = 1) ORDER BY ts DESC'))])\n        elif IS_CRDB:\n            assertViews([\n                ('notes_deleted',\n                 ('SELECT content FROM peewee_test.public.notes '\n                  'WHERE status = 9 ORDER BY id DESC')),\n                ('notes_public',\n                 ('SELECT content, ts FROM peewee_test.public.notes '\n                  'WHERE status = 1 ORDER BY ts DESC'))])\n\n    @requires_models(User, Tweet, Category)\n    def test_get_foreign_keys(self):\n        foreign_keys = self.database.get_foreign_keys('tweet')\n        data = [(fk.column, fk.dest_table, fk.dest_column, fk.table)\n                for fk in foreign_keys]\n        self.assertEqual(data, [\n            ('user_id', 'users', 'id', 'tweet')])\n\n        foreign_keys = self.database.get_foreign_keys('category')\n        data = [(fk.column, fk.dest_table, fk.dest_column, fk.table)\n                for fk in foreign_keys]\n        self.assertEqual(data, [\n            ('parent_id', 'category', 'name', 'category')])\n\n\nclass TestSortModels(BaseTestCase):\n    def test_sort_models(self):\n        class A(Model):\n            pass\n        class B(Model):\n            a = ForeignKeyField(A)\n        class C(Model):\n            b = ForeignKeyField(B)\n        class D(Model):\n            c = ForeignKeyField(C)\n        class E(Model):\n            pass\n\n        models = [A, B, C, D, E]\n        for list_of_models in permutations(models):\n            sorted_models = sort_models(list_of_models)\n            self.assertEqual(sorted_models, models)\n\n    def test_sort_models_multi_fk(self):\n        class Inventory(Model):\n            pass\n        class Sheet(Model):\n            inventory = ForeignKeyField(Inventory)\n        class Program(Model):\n            inventory = ForeignKeyField(Inventory)\n        class ProgramSheet(Model):\n            program = ForeignKeyField(Program)\n            sheet = ForeignKeyField(Sheet)\n        class ProgramPart(Model):\n            program_sheet = ForeignKeyField(ProgramSheet)\n        class Offal(Model):\n            program_sheet = ForeignKeyField(ProgramSheet)\n            sheet = ForeignKeyField(Sheet)\n\n        M = [Inventory, Sheet, Program, ProgramSheet, ProgramPart, Offal]\n        sorted_models = sort_models(M)\n        self.assertEqual(sorted_models, [\n            Inventory,\n            Program,\n            Sheet,\n            ProgramSheet,\n            Offal,\n            ProgramPart,\n        ])\n        for list_of_models in permutations(M):\n            self.assertEqual(sort_models(list_of_models), sorted_models)\n\n\nclass TestDBProxy(BaseTestCase):\n    def test_proxy_context_manager(self):\n        db = Proxy()\n        class User(Model):\n            username = TextField()\n\n            class Meta:\n                database = db\n\n        self.assertRaises(AttributeError, User.create_table)\n\n        sqlite_db = SqliteDatabase(':memory:')\n        db.initialize(sqlite_db)\n        User.create_table()\n        with db:\n            self.assertFalse(db.is_closed())\n        self.assertTrue(db.is_closed())\n\n    def test_db_proxy(self):\n        db = Proxy()\n        class BaseModel(Model):\n            class Meta:\n                database = db\n\n        class User(BaseModel):\n            username = TextField()\n\n        class Tweet(BaseModel):\n            user = ForeignKeyField(User, backref='tweets')\n            message = TextField()\n\n        sqlite_db = SqliteDatabase(':memory:')\n        db.initialize(sqlite_db)\n\n        self.assertEqual(User._meta.database.database, ':memory:')\n        self.assertEqual(Tweet._meta.database.database, ':memory:')\n\n        self.assertTrue(User._meta.database.is_closed())\n        self.assertTrue(Tweet._meta.database.is_closed())\n        sqlite_db.connect()\n        self.assertFalse(User._meta.database.is_closed())\n        self.assertFalse(Tweet._meta.database.is_closed())\n        sqlite_db.close()\n\n    def test_proxy_decorator(self):\n        db = DatabaseProxy()\n\n        @db.connection_context()\n        def with_connection():\n            self.assertFalse(db.is_closed())\n\n        @db.atomic()\n        def with_transaction():\n            self.assertTrue(db.in_transaction())\n\n        @db.manual_commit()\n        def with_manual_commit():\n            self.assertTrue(db.in_transaction())\n\n        db.initialize(SqliteDatabase(':memory:'))\n        with_connection()\n        self.assertTrue(db.is_closed())\n        with_transaction()\n        self.assertFalse(db.in_transaction())\n        with_manual_commit()\n        self.assertFalse(db.in_transaction())\n\n    def test_proxy_bind_ctx_callbacks(self):\n        db = Proxy()\n        class BaseModel(Model):\n            class Meta:\n                database = db\n\n        class Hook(BaseModel):\n            data = BlobField()  # Attaches hook to configure blob-type.\n\n        self.assertTrue(Hook.data._constructor is bytearray)\n\n        class CustomSqliteDB(SqliteDatabase):\n            sentinel = object()\n            def get_binary_type(self):\n                return self.sentinel\n\n        custom_db = CustomSqliteDB(':memory:')\n\n        with custom_db.bind_ctx([Hook]):\n            self.assertTrue(Hook.data._constructor is custom_db.sentinel)\n\n        self.assertTrue(Hook.data._constructor is bytearray)\n\n        custom_db.bind([Hook])\n        self.assertTrue(Hook.data._constructor is custom_db.sentinel)\n\n\nclass Data(TestModel):\n    key = TextField()\n    value = TextField()\n\n    class Meta:\n        schema = 'main'\n\n\nclass TestAttachDatabase(ModelTestCase):\n    database = get_sqlite_db()\n    requires = [Data]\n\n    def test_attach(self):\n        database = self.database\n        Data.create(key='k1', value='v1')\n        Data.create(key='k2', value='v2')\n\n        # Attach an in-memory cache database.\n        database.attach(':memory:', 'cache')\n\n        # Clone data into the in-memory cache.\n        class CacheData(Data):\n            class Meta:\n                schema = 'cache'\n\n        self.assertFalse(CacheData.table_exists())\n        CacheData.create_table(safe=False)\n        self.assertTrue(CacheData.table_exists())\n\n        (CacheData\n         .insert_from(Data.select(), fields=[Data.id, Data.key, Data.value])\n         .execute())\n\n        # Update the source data.\n        query = Data.update({Data.value: Data.value + '-x'})\n        self.assertEqual(query.execute(), 2)\n\n        # Verify the source data was updated.\n        query = Data.select(Data.key, Data.value).order_by(Data.key)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"key\", \"t1\".\"value\" '\n            'FROM \"main\".\"data\" AS \"t1\" '\n            'ORDER BY \"t1\".\"key\"'), [])\n        self.assertEqual([v for k, v in query.tuples()], ['v1-x', 'v2-x'])\n\n        # Verify the cached data reflects the original data, pre-update.\n        query = (CacheData\n                 .select(CacheData.key, CacheData.value)\n                 .order_by(CacheData.key))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"key\", \"t1\".\"value\" '\n            'FROM \"cache\".\"cache_data\" AS \"t1\" '\n            'ORDER BY \"t1\".\"key\"'), [])\n        self.assertEqual([v for k, v in query.tuples()], ['v1', 'v2'])\n\n        database.close()\n\n        # On re-connecting, the in-memory database will re-attached.\n        database.connect()\n\n        # Cache-Data table does not exist.\n        self.assertFalse(CacheData.table_exists())\n\n        # Double-check the sqlite master table.\n        curs = database.execute_sql('select * from cache.sqlite_master;')\n        self.assertEqual(curs.fetchall(), [])\n\n        # Because it's in-memory, the table needs to be re-created.\n        CacheData.create_table(safe=False)\n        self.assertEqual(CacheData.select().count(), 0)\n\n        # Original data is still there.\n        self.assertEqual(Data.select().count(), 2)\n\n    def test_attach_detach(self):\n        database = self.database\n        Data.create(key='k1', value='v1')\n        Data.create(key='k2', value='v2')\n\n        # Attach an in-memory cache database.\n        database.attach(':memory:', 'cache')\n        curs = database.execute_sql('select * from cache.sqlite_master')\n        self.assertEqual(curs.fetchall(), [])\n\n        self.assertFalse(database.attach(':memory:', 'cache'))\n        self.assertRaises(OperationalError, database.attach, 'foo.db', 'cache')\n\n        self.assertTrue(database.detach('cache'))\n        self.assertFalse(database.detach('cache'))\n        self.assertRaises(OperationalError, database.execute_sql,\n                          'select * from cache.sqlite_master')\n\n    def test_sqlite_schema_support(self):\n        class CacheData(Data):\n            class Meta:\n                schema = 'cache'\n\n        # Attach an in-memory cache database and create the cache table.\n        self.database.attach(':memory:', 'cache')\n        CacheData.create_table()\n\n        tables = self.database.get_tables()\n        self.assertEqual(tables, ['data'])\n\n        tables = self.database.get_tables(schema='cache')\n        self.assertEqual(tables, ['cache_data'])\n\n\nclass TestDatabaseConnection(DatabaseTestCase):\n    def test_is_connection_usable(self):\n        # Ensure a connection is open.\n        conn = self.database.connection()\n        self.assertTrue(self.database.is_connection_usable())\n\n        self.database.close()\n        self.assertFalse(self.database.is_connection_usable())\n        self.database.connect()\n        self.assertTrue(self.database.is_connection_usable())\n\n    @requires_postgresql\n    def test_is_connection_usable_pg(self):\n        self.database.execute_sql('drop table if exists foo')\n        self.database.execute_sql('create table foo (data text not null)')\n        self.assertTrue(self.database.is_connection_usable())\n\n        with self.database.atomic() as txn:\n            with self.assertRaises(IntegrityError):\n                self.database.execute_sql('insert into foo (data) values (NULL)')\n\n            self.assertFalse(self.database.is_closed())\n            self.assertFalse(self.database.is_connection_usable())\n            txn.rollback()\n            self.assertTrue(self.database.is_connection_usable())\n\n            curs = self.database.execute_sql('select * from foo')\n            self.assertEqual(list(curs), [])\n            self.database.execute_sql('drop table foo')\n\n\nclass TestExceptionWrapper(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User]\n\n    def test_exception_wrapper(self):\n        exc = None\n        try:\n            User.create(username=None)\n        except IntegrityError as e:\n            exc = e\n\n        if exc is None: raise Exception('expected integrity error not raised')\n        self.assertTrue(exc.orig.__module__ != 'peewee')\n\n\nclass TestModelPropertyHelper(BaseTestCase):\n    def test_model_property(self):\n        database = get_in_memory_db()\n        class M1(database.Model): pass\n        class M2(database.Model): pass\n        class CM1(M1): pass\n        for M in (M1, M2, CM1):\n            self.assertTrue(M._meta.database is database)\n\n    def test_model_property_on_proxy(self):\n        db = DatabaseProxy()\n        class M1(db.Model): pass\n        class M2(db.Model): pass\n        class CM1(M1): pass\n\n        test_db = get_in_memory_db()\n        db.initialize(test_db)\n        for M in (M1, M2, CM1):\n            self.assertEqual(M._meta.database.database, ':memory:')\n\n\nclass TestChunkedUtility(BaseTestCase):\n    def test_chunked_exact_divisor(self):\n        result = list(chunked(range(6), 3))\n        self.assertEqual(result, [[0, 1, 2], [3, 4, 5]])\n\n    def test_chunked_with_remainder(self):\n        result = list(chunked(range(7), 3))\n        self.assertEqual(result, [[0, 1, 2], [3, 4, 5], [6]])\n\n    def test_chunked_single_element(self):\n        result = list(chunked([42], 5))\n        self.assertEqual(result, [[42]])\n\n    def test_chunked_empty(self):\n        result = list(chunked([], 5))\n        self.assertEqual(result, [])\n\n    def test_chunked_size_one(self):\n        result = list(chunked(range(3), 1))\n        self.assertEqual(result, [[0], [1], [2]])\n\n    def test_chunked_generator_input(self):\n        gen = (x * 2 for x in range(5))\n        result = list(chunked(gen, 2))\n        self.assertEqual(result, [[0, 2], [4, 6], [8]])\n"
  },
  {
    "path": "tests/db_url.py",
    "content": "from peewee import *\nfrom playhouse.db_url import connect\nfrom playhouse.db_url import parse\n\nfrom .base import BaseTestCase\n\n\nclass TestDBUrl(BaseTestCase):\n    def test_db_url_parse(self):\n        cfg = parse('mysql://usr:pwd@hst:123/db')\n        self.assertEqual(cfg['user'], 'usr')\n        self.assertEqual(cfg['passwd'], 'pwd')\n        self.assertEqual(cfg['host'], 'hst')\n        self.assertEqual(cfg['database'], 'db')\n        self.assertEqual(cfg['port'], 123)\n        cfg = parse('postgresql://usr:pwd@hst/db')\n        self.assertEqual(cfg['password'], 'pwd')\n        cfg = parse('mysql+pool://usr:pwd@hst:123/db'\n                    '?max_connections=42&stale_timeout=8001.2&zai=&baz=3.4.5'\n                    '&boolz=false')\n        self.assertEqual(cfg['user'], 'usr')\n        self.assertEqual(cfg['password'], 'pwd')\n        self.assertEqual(cfg['host'], 'hst')\n        self.assertEqual(cfg['database'], 'db')\n        self.assertEqual(cfg['port'], 123)\n        self.assertEqual(cfg['max_connections'], 42)\n        self.assertEqual(cfg['stale_timeout'], 8001.2)\n        self.assertEqual(cfg['zai'], '')\n        self.assertEqual(cfg['baz'], '3.4.5')\n        self.assertEqual(cfg['boolz'], False)\n\n    def test_db_url_no_unquoting(self):\n        # By default, neither user nor password is not unescaped.\n        cfg = parse('mysql://usr%40example.com:pwd%23@hst:123/db')\n        self.assertEqual(cfg['user'], 'usr%40example.com')\n        self.assertEqual(cfg['passwd'], 'pwd%23')\n        self.assertEqual(cfg['host'], 'hst')\n        self.assertEqual(cfg['database'], 'db')\n        self.assertEqual(cfg['port'], 123)\n\n    def test_db_url_quoted_password(self):\n        cfg = parse('mysql://usr:pwd%23%20@hst:123/db', unquote_password=True)\n        self.assertEqual(cfg['user'], 'usr')\n        self.assertEqual(cfg['passwd'], 'pwd# ')\n        self.assertEqual(cfg['host'], 'hst')\n        self.assertEqual(cfg['database'], 'db')\n        self.assertEqual(cfg['port'], 123)\n\n    def test_db_url_quoted_user(self):\n        cfg = parse('mysql://usr%40example.com:p%40sswd@hst:123/db', unquote_user=True)\n        self.assertEqual(cfg['user'], 'usr@example.com')\n        self.assertEqual(cfg['passwd'], 'p%40sswd')\n        self.assertEqual(cfg['host'], 'hst')\n        self.assertEqual(cfg['database'], 'db')\n        self.assertEqual(cfg['port'], 123)\n\n    def test_db_url(self):\n        db = connect('sqlite:///:memory:')\n        self.assertTrue(isinstance(db, SqliteDatabase))\n        self.assertEqual(db.database, ':memory:')\n\n        db = connect('sqlite:///:memory:', pragmas=(\n            ('journal_mode', 'MEMORY'),))\n        self.assertTrue(('journal_mode', 'MEMORY') in db._pragmas)\n\n        #db = connect('sqliteext:///foo/bar.db')\n        #self.assertTrue(isinstance(db, SqliteExtDatabase))\n        #self.assertEqual(db.database, 'foo/bar.db')\n\n        db = connect('sqlite:////this/is/absolute.path')\n        self.assertEqual(db.database, '/this/is/absolute.path')\n\n        db = connect('sqlite://')\n        self.assertTrue(isinstance(db, SqliteDatabase))\n        self.assertEqual(db.database, ':memory:')\n\n        db = connect('sqlite:///test.db?p1=1?a&p2=22&p3=xyz')\n        self.assertTrue(isinstance(db, SqliteDatabase))\n        self.assertEqual(db.database, 'test.db')\n        self.assertEqual(db.connect_params, {\n            'p1': '1?a', 'p2': 22, 'p3': 'xyz'})\n\n    def test_bad_scheme(self):\n        def _test_scheme():\n            connect('missing:///')\n\n        self.assertRaises(RuntimeError, _test_scheme)\n"
  },
  {
    "path": "tests/expressions.py",
    "content": "from peewee import *\n\nfrom .base import IS_SQLITE\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import get_in_memory_db\nfrom .base import skip_if\n\n\nclass Person(TestModel):\n    name = CharField()\n\n\nclass BaseNamesTest(ModelTestCase):\n    requires = [Person]\n\n    def assertNames(self, exp, x):\n        query = Person.select().where(exp).order_by(Person.name)\n        self.assertEqual([p.name for p in query], x)\n\n\n\nclass TestRegexp(BaseNamesTest):\n    @skip_if(IS_SQLITE)\n    def test_regexp_iregexp(self):\n        people = [Person.create(name=name) for name in ('n1', 'n2', 'n3')]\n\n        self.assertNames(Person.name.regexp('n[1,3]'), ['n1', 'n3'])\n        self.assertNames(Person.name.regexp('N[1,3]'), [])\n        self.assertNames(Person.name.iregexp('n[1,3]'), ['n1', 'n3'])\n        self.assertNames(Person.name.iregexp('N[1,3]'), ['n1', 'n3'])\n\n\nclass TestContains(BaseNamesTest):\n    def test_contains_startswith_endswith(self):\n        people = [Person.create(name=n) for n in ('huey', 'mickey', 'zaizee')]\n\n        self.assertNames(Person.name.contains('ey'), ['huey', 'mickey'])\n        self.assertNames(Person.name.contains('EY'), ['huey', 'mickey'])\n\n        self.assertNames(Person.name.startswith('m'), ['mickey'])\n        self.assertNames(Person.name.startswith('M'), ['mickey'])\n\n        self.assertNames(Person.name.endswith('ey'), ['huey', 'mickey'])\n        self.assertNames(Person.name.endswith('EY'), ['huey', 'mickey'])\n\n\nclass UpperField(TextField):\n    def db_value(self, value):\n        return fn.UPPER(value)\n\n\nclass UpperModel(TestModel):\n    name = UpperField()\n\n\nclass TestValueConversion(ModelTestCase):\n    \"\"\"\n    Test the conversion of field values using a field's db_value() function.\n\n    It is possible that a field's `db_value()` function may returns a Node\n    subclass (e.g. a SQL function). These tests verify and document how such\n    conversions are applied in various parts of the query.\n    \"\"\"\n    database = get_in_memory_db()\n    requires = [UpperModel]\n\n    def test_value_conversion(self):\n        # Ensure value is converted on INSERT.\n        insert = UpperModel.insert({UpperModel.name: 'huey'})\n        self.assertSQL(insert, (\n            'INSERT INTO \"upper_model\" (\"name\") VALUES (UPPER(?))'), ['huey'])\n        uid = insert.execute()\n\n        obj = UpperModel.get(UpperModel.id == uid)\n        self.assertEqual(obj.name, 'HUEY')\n\n        # Ensure value is converted on UPDATE.\n        update = (UpperModel\n                  .update({UpperModel.name: 'zaizee'})\n                  .where(UpperModel.id == uid))\n        self.assertSQL(update, (\n            'UPDATE \"upper_model\" SET \"name\" = UPPER(?) '\n            'WHERE (\"upper_model\".\"id\" = ?)'),\n            ['zaizee', uid])\n        update.execute()\n\n        # Ensure it works with SELECT (or more generally, WHERE expressions).\n        select = UpperModel.select().where(UpperModel.name == 'zaizee')\n        self.assertSQL(select, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = UPPER(?))'), ['zaizee'])\n        obj = select.get()\n        self.assertEqual(obj.name, 'ZAIZEE')\n\n        # Ensure it works with DELETE.\n        delete = UpperModel.delete().where(UpperModel.name == 'zaizee')\n        self.assertSQL(delete, (\n            'DELETE FROM \"upper_model\" '\n            'WHERE (\"upper_model\".\"name\" = UPPER(?))'), ['zaizee'])\n        self.assertEqual(delete.execute(), 1)\n\n    def test_value_conversion_mixed(self):\n        um = UpperModel.create(name='huey')\n\n        # If we apply a function to the field, the conversion is not applied.\n        sq = UpperModel.select().where(fn.SUBSTR(UpperModel.name, 1, 1) == 'h')\n        self.assertSQL(sq, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'WHERE (SUBSTR(\"t1\".\"name\", ?, ?) = ?)'), [1, 1, 'h'])\n        self.assertRaises(UpperModel.DoesNotExist, sq.get)\n\n        # If we encapsulate the object as a value, the conversion is applied.\n        sq = UpperModel.select().where(UpperModel.name == Value('huey'))\n        self.assertSQL(sq, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = UPPER(?))'), ['huey'])\n        self.assertEqual(sq.get().id, um.id)\n\n        # Unless we explicitly pass converter=False.\n        sq = UpperModel.select().where(UpperModel.name == Value('huey', False))\n        self.assertSQL(sq, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = ?)'), ['huey'])\n        self.assertRaises(UpperModel.DoesNotExist, sq.get)\n\n        # If we specify explicit SQL on the rhs, the conversion is not applied.\n        sq = UpperModel.select().where(UpperModel.name == SQL('?', ['huey']))\n        self.assertSQL(sq, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = ?)'), ['huey'])\n        self.assertRaises(UpperModel.DoesNotExist, sq.get)\n\n        # Function arguments are not coerced.\n        sq = UpperModel.select().where(UpperModel.name == fn.LOWER('huey'))\n        self.assertSQL(sq, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = LOWER(?))'), ['huey'])\n        self.assertRaises(UpperModel.DoesNotExist, sq.get)\n\n    def test_value_conversion_query(self):\n        um = UpperModel.create(name='huey')\n        UM = UpperModel.alias()\n        subq = UM.select(UM.name).where(UM.name == 'huey')\n\n        # Select from WHERE ... IN <subquery>.\n        query = UpperModel.select().where(UpperModel.name.in_(subq))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" IN ('\n            'SELECT \"t2\".\"name\" FROM \"upper_model\" AS \"t2\" '\n            'WHERE (\"t2\".\"name\" = UPPER(?))))'), ['huey'])\n        self.assertEqual(query.get().id, um.id)\n\n        # Join on sub-query.\n        query = (UpperModel\n                 .select()\n                 .join(subq, on=(UpperModel.name == subq.c.name)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"upper_model\" AS \"t1\" '\n            'INNER JOIN (SELECT \"t2\".\"name\" FROM \"upper_model\" AS \"t2\" '\n            'WHERE (\"t2\".\"name\" = UPPER(?))) AS \"t3\" '\n            'ON (\"t1\".\"name\" = \"t3\".\"name\")'), ['huey'])\n        row = query.tuples().get()\n        self.assertEqual(row, (um.id, 'HUEY'))\n\n    def test_having_clause(self):\n        query = (UpperModel\n                 .select(UpperModel.name, fn.COUNT(UpperModel.id).alias('ct'))\n                 .group_by(UpperModel.name)\n                 .having(UpperModel.name == 'huey'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\", COUNT(\"t1\".\"id\") AS \"ct\" '\n            'FROM \"upper_model\" AS \"t1\" '\n            'GROUP BY \"t1\".\"name\" '\n            'HAVING (\"t1\".\"name\" = UPPER(?))'), ['huey'])\n"
  },
  {
    "path": "tests/extra_fields.py",
    "content": "from peewee import *\nfrom playhouse.fields import CompressedField\nfrom playhouse.fields import PickleField\n\nfrom .base import db\nfrom .base import ModelTestCase\nfrom .base import TestModel\n\n\nclass Comp(TestModel):\n    key = TextField()\n    data = CompressedField()\n\n\nclass Pickled(TestModel):\n    key = TextField()\n    data = PickleField()\n\n\nclass TestCompressedField(ModelTestCase):\n    requires = [Comp]\n\n    def test_compressed_field(self):\n        a = b'a' * 1024\n        b = b'b' * 1024\n        Comp.create(data=a, key='a')\n        Comp.create(data=b, key='b')\n\n        a_db = Comp.get(Comp.key == 'a')\n        self.assertEqual(a_db.data, a)\n\n        b_db = Comp.get(Comp.key == 'b')\n        self.assertEqual(b_db.data, b)\n\n        # Get at the underlying data.\n        CompTbl = Table('comp', ('id', 'data', 'key')).bind(self.database)\n        obj = CompTbl.select().where(CompTbl.key == 'a').get()\n        self.assertEqual(obj['key'], 'a')\n\n        # Ensure that the data actually was compressed.\n        self.assertTrue(len(obj['data']) < 1024)\n\n\nclass TestPickleField(ModelTestCase):\n    requires = [Pickled]\n\n    def test_pickle_field(self):\n        a = {'k1': 'v1', 'k2': [0, 1, 2], 'k3': None}\n        b = 'just a string'\n        Pickled.create(data=a, key='a')\n        Pickled.create(data=b, key='b')\n\n        a_db = Pickled.get(Pickled.key == 'a')\n        self.assertEqual(a_db.data, a)\n\n        b_db = Pickled.get(Pickled.key == 'b')\n        self.assertEqual(b_db.data, b)\n"
  },
  {
    "path": "tests/fields.py",
    "content": "import calendar\nimport datetime\nimport sqlite3\nimport time\nimport uuid\nfrom decimal import Decimal as D\nfrom decimal import ROUND_UP\n\nfrom peewee import NodeList\nfrom peewee import VirtualField\nfrom peewee import *\n\nfrom .base import BaseTestCase\nfrom .base import IS_CRDB\nfrom .base import IS_MYSQL\nfrom .base import IS_POSTGRESQL\nfrom .base import IS_SQLITE\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db\nfrom .base import get_in_memory_db\nfrom .base import requires_models\nfrom .base import requires_mysql\nfrom .base import requires_pglike\nfrom .base import requires_sqlite\nfrom .base import skip_if\nfrom .base_models import Tweet\nfrom .base_models import User\n\n\nclass IntModel(TestModel):\n    value = IntegerField()\n    value_null = IntegerField(null=True)\n\n\nclass TestCoerce(ModelTestCase):\n    requires = [IntModel]\n\n    def test_coerce(self):\n        i = IntModel.create(value='1337', value_null=3.14159)\n        i_db = IntModel.get(IntModel.id == i.id)\n        self.assertEqual(i_db.value, 1337)\n        self.assertEqual(i_db.value_null, 3)\n\n\nclass DefaultValues(TestModel):\n    data = IntegerField(default=17)\n    data_callable = IntegerField(default=lambda: 1337)\n\n\nclass TestTextField(TextField):\n    def first_char(self):\n        return fn.SUBSTR(self, 1, 1)\n\n\nclass PhoneBook(TestModel):\n    name = TestTextField()\n\n\nclass Bits(TestModel):\n    F_STICKY = 1\n    F_FAVORITE = 2\n    F_MINIMIZED = 4\n\n    flags = BitField()\n    is_sticky = flags.flag(F_STICKY)\n    is_favorite = flags.flag(F_FAVORITE)\n    is_minimized = flags.flag(F_MINIMIZED)\n\n    data = BigBitField()\n\n\nclass TestDefaultValues(ModelTestCase):\n    requires = [DefaultValues]\n\n    def test_default_values(self):\n        d = DefaultValues()\n        self.assertEqual(d.data, 17)\n        self.assertEqual(d.data_callable, 1337)\n        d.save()\n\n        d_db = DefaultValues.get(DefaultValues.id == d.id)\n        self.assertEqual(d_db.data, 17)\n        self.assertEqual(d_db.data_callable, 1337)\n\n    def test_defaults_create(self):\n        d = DefaultValues.create()\n        self.assertEqual(d.data, 17)\n        self.assertEqual(d.data_callable, 1337)\n\n        d_db = DefaultValues.get(DefaultValues.id == d.id)\n        self.assertEqual(d_db.data, 17)\n        self.assertEqual(d_db.data_callable, 1337)\n\n\nclass TestNullConstraint(ModelTestCase):\n    requires = [IntModel]\n\n    def test_null(self):\n        i = IntModel.create(value=1)\n        i_db = IntModel.get(IntModel.value == 1)\n        self.assertIsNone(i_db.value_null)\n\n    def test_empty_value(self):\n        with self.database.atomic():\n            with self.assertRaisesCtx(IntegrityError):\n                IntModel.create(value=None)\n\n\nclass TestIntegerField(ModelTestCase):\n    requires = [IntModel]\n\n    def test_integer_field(self):\n        i1 = IntModel.create(value=1)\n        i2 = IntModel.create(value=2, value_null=20)\n\n        vals = [(i.value, i.value_null)\n                for i in IntModel.select().order_by(IntModel.value)]\n        self.assertEqual(vals, [\n            (1, None),\n            (2, 20)])\n\n\nclass FloatModel(TestModel):\n    value = FloatField()\n    value_null = FloatField(null=True)\n\n\nclass TestFloatField(ModelTestCase):\n    requires = [FloatModel]\n\n    def test_float_field(self):\n        f1 = FloatModel.create(value=1.23)\n        f2 = FloatModel.create(value=3.14, value_null=0.12)\n\n        query = FloatModel.select().order_by(FloatModel.id)\n        self.assertEqual([(f.value, f.value_null) for f in query],\n                         [(1.23, None), (3.14, 0.12)])\n\n\nclass DecimalModel(TestModel):\n    value = DecimalField(decimal_places=2, auto_round=True)\n    value_up = DecimalField(decimal_places=2, auto_round=True,\n                            rounding=ROUND_UP, null=True)\n\n\nclass TestDecimalField(ModelTestCase):\n    requires = [DecimalModel]\n\n    def test_decimal_field(self):\n        d1 = DecimalModel.create(value=D('3'))\n        d2 = DecimalModel.create(value=D('100.33'))\n\n        self.assertEqual(sorted(d.value for d in DecimalModel.select()),\n                         [D('3'), D('100.33')])\n\n    def test_decimal_rounding(self):\n        d = DecimalModel.create(value=D('1.2345'), value_up=D('1.2345'))\n        d_db = DecimalModel.get(DecimalModel.id == d.id)\n        self.assertEqual(d_db.value, D('1.23'))\n        self.assertEqual(d_db.value_up, D('1.24'))\n\n\nclass BoolModel(TestModel):\n    value = BooleanField(null=True)\n    name = CharField()\n\n\nclass TestBooleanField(ModelTestCase):\n    requires = [BoolModel]\n\n    def test_boolean_field(self):\n        BoolModel.create(value=True, name='t')\n        BoolModel.create(value=False, name='f')\n        BoolModel.create(value=None, name='n')\n\n        vals = sorted((b.name, b.value) for b in BoolModel.select())\n        self.assertEqual(vals, [\n            ('f', False),\n            ('n', None),\n            ('t', True)])\n\n\nclass DateModel(TestModel):\n    date = DateField(null=True)\n    time = TimeField(null=True)\n    date_time = DateTimeField(null=True)\n\n\nclass CustomDateTimeModel(TestModel):\n    date_time = DateTimeField(formats=[\n        '%m/%d/%Y %I:%M %p',\n        '%Y-%m-%d %H:%M:%S'])\n\n\nclass TestDateFields(ModelTestCase):\n    requires = [DateModel]\n\n    @requires_models(CustomDateTimeModel)\n    def test_date_time_custom_format(self):\n        cdtm = CustomDateTimeModel.create(date_time='01/02/2003 01:37 PM')\n        cdtm_db = CustomDateTimeModel[cdtm.id]\n        self.assertEqual(cdtm_db.date_time,\n                         datetime.datetime(2003, 1, 2, 13, 37, 0))\n\n    def test_date_fields(self):\n        dt1 = datetime.datetime(2011, 1, 2, 11, 12, 13, 54321)\n        dt2 = datetime.datetime(2011, 1, 2, 11, 12, 13)\n        d1 = datetime.date(2011, 1, 3)\n        t1 = datetime.time(11, 12, 13, 54321)\n        t2 = datetime.time(11, 12, 13)\n\n        if isinstance(self.database, MySQLDatabase):\n            dt1 = dt1.replace(microsecond=0)\n            t1 = t1.replace(microsecond=0)\n\n        dm1 = DateModel.create(date_time=dt1, date=d1, time=t1)\n        dm2 = DateModel.create(date_time=dt2, time=t2)\n\n        dm1_db = DateModel.get(DateModel.id == dm1.id)\n        self.assertEqual(dm1_db.date, d1)\n        self.assertEqual(dm1_db.date_time, dt1)\n        self.assertEqual(dm1_db.time, t1)\n\n        dm2_db = DateModel.get(DateModel.id == dm2.id)\n        self.assertEqual(dm2_db.date, None)\n        self.assertEqual(dm2_db.date_time, dt2)\n        self.assertEqual(dm2_db.time, t2)\n\n    def test_extract_parts(self):\n        dm = DateModel.create(\n            date_time=datetime.datetime(2011, 1, 2, 11, 12, 13, 54321),\n            date=datetime.date(2012, 2, 3),\n            time=datetime.time(3, 13, 37))\n        query = (DateModel\n                 .select(DateModel.date_time.year, DateModel.date_time.month,\n                         DateModel.date_time.day, DateModel.date_time.hour,\n                         DateModel.date_time.minute,\n                         DateModel.date_time.second, DateModel.date.year,\n                         DateModel.date.month, DateModel.date.day,\n                         DateModel.time.hour, DateModel.time.minute,\n                         DateModel.time.second)\n                 .tuples())\n\n        row, = query\n        if IS_SQLITE or IS_MYSQL:\n            self.assertEqual(row,\n                             (2011, 1, 2, 11, 12, 13, 2012, 2, 3, 3, 13, 37))\n        else:\n            self.assertTrue(row in [\n                (2011., 1., 2., 11., 12., 13.054321, 2012., 2., 3., 3., 13.,\n                 37.),\n                (D('2011'), D('1'), D('2'), D('11'), D('12'), D('13.054321'),\n                 D('2012'), D('2'), D('3'), D('3'), D('13'), D('37'))])\n\n    def test_truncate_date(self):\n        dm = DateModel.create(\n            date_time=datetime.datetime(2001, 2, 3, 4, 5, 6, 7),\n            date=datetime.date(2002, 3, 4))\n\n        accum = []\n        for p in ('year', 'month', 'day', 'hour', 'minute', 'second'):\n            accum.append(DateModel.date_time.truncate(p))\n        for p in ('year', 'month', 'day'):\n            accum.append(DateModel.date.truncate(p))\n\n        query = DateModel.select(*accum).tuples()\n        data = list(query[0])\n\n        # Postgres includes timezone info, so strip that for comparison.\n        if IS_POSTGRESQL or IS_CRDB:\n            data = [dt.replace(tzinfo=None) for dt in data]\n\n        self.assertEqual(data, [\n            datetime.datetime(2001, 1, 1, 0, 0, 0),\n            datetime.datetime(2001, 2, 1, 0, 0, 0),\n            datetime.datetime(2001, 2, 3, 0, 0, 0),\n            datetime.datetime(2001, 2, 3, 4, 0, 0),\n            datetime.datetime(2001, 2, 3, 4, 5, 0),\n            datetime.datetime(2001, 2, 3, 4, 5, 6),\n            datetime.datetime(2002, 1, 1, 0, 0, 0),\n            datetime.datetime(2002, 3, 1, 0, 0, 0),\n            datetime.datetime(2002, 3, 4, 0, 0, 0)])\n\n    def test_to_timestamp(self):\n        dt = datetime.datetime(2019, 1, 2, 3, 4, 5)\n        ts = calendar.timegm(dt.utctimetuple())\n\n        dt2 = datetime.datetime(2019, 1, 3)\n        ts2 = calendar.timegm(dt2.utctimetuple())\n\n        DateModel.create(date_time=dt, date=dt2.date())\n\n        query = DateModel.select(\n            DateModel.id,\n            DateModel.date_time.to_timestamp().alias('dt_ts'),\n            DateModel.date.to_timestamp().alias('dt2_ts'))\n        obj = query.get()\n\n        self.assertEqual(obj.dt_ts, ts)\n        self.assertEqual(obj.dt2_ts, ts2)\n\n        ts3 = ts + 86400\n        query = (DateModel.select()\n                 .where((DateModel.date_time.to_timestamp() + 86400) < ts3))\n        self.assertRaises(DateModel.DoesNotExist, query.get)\n\n        query = (DateModel.select()\n                 .where((DateModel.date.to_timestamp() + 86400) > ts3))\n        self.assertEqual(query.get().id, obj.id)\n\n    def test_distinct_date_part(self):\n        years = (1980, 1990, 2000, 2010)\n        for i, year in enumerate(years):\n            for j in range(i + 1):\n                DateModel.create(date=datetime.date(year, i + 1, 1))\n\n        query = (DateModel\n                 .select(DateModel.date.year.distinct())\n                 .order_by(DateModel.date.year))\n        self.assertEqual([year for year, in query.tuples()],\n                         [1980, 1990, 2000, 2010])\n\n\nclass U2(TestModel):\n    username = TextField()\n\n\nclass T2(TestModel):\n    user = ForeignKeyField(U2, backref='tweets', on_delete='CASCADE')\n    content = TextField()\n\n\nclass TestForeignKeyField(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_set_fk(self):\n        huey = User.create(username='huey')\n        zaizee = User.create(username='zaizee')\n\n        # Test resolution of attributes after creation does not trigger SELECT.\n        with self.assertQueryCount(1):\n            tweet = Tweet.create(content='meow', user=huey)\n            self.assertEqual(tweet.user.username, 'huey')\n\n        # Test we can set to an integer, in which case a query will occur.\n        with self.assertQueryCount(2):\n            tweet = Tweet.create(content='purr', user=zaizee.id)\n            self.assertEqual(tweet.user.username, 'zaizee')\n\n        # Test we can set the ID accessor directly.\n        with self.assertQueryCount(2):\n            tweet = Tweet.create(content='hiss', user_id=huey.id)\n            self.assertEqual(tweet.user.username, 'huey')\n\n    def test_follow_attributes(self):\n        huey = User.create(username='huey')\n        Tweet.create(content='meow', user=huey)\n        Tweet.create(content='hiss', user=huey)\n\n        with self.assertQueryCount(1):\n            query = (Tweet\n                     .select(Tweet.content, Tweet.user.username)\n                     .join(User)\n                     .order_by(Tweet.content))\n            self.assertEqual([(tweet.content, tweet.user.username)\n                              for tweet in query],\n                             [('hiss', 'huey'), ('meow', 'huey')])\n\n        self.assertRaises(AttributeError, lambda: Tweet.user.foo)\n\n    def test_disable_backref(self):\n        class Person(TestModel):\n            pass\n        class Pet(TestModel):\n            owner = ForeignKeyField(Person, backref='!')\n\n        self.assertEqual(Pet.owner.backref, '!')\n\n        # No attribute/accessor is added to the related model.\n        self.assertRaises(AttributeError, lambda: Person.pet_set)\n\n        # We still preserve the metadata about the relationship.\n        self.assertTrue(Pet.owner in Person._meta.backrefs)\n\n    @requires_models(U2, T2)\n    def test_on_delete_behavior(self):\n        if IS_SQLITE:\n            self.database.foreign_keys = 1\n\n        with self.database.atomic():\n            for username in ('u1', 'u2', 'u3'):\n                user = U2.create(username=username)\n                for i in range(3):\n                    T2.create(user=user, content='%s-%s' % (username, i))\n\n        self.assertEqual(T2.select().count(), 9)\n        U2.delete().where(U2.username == 'u2').execute()\n        self.assertEqual(T2.select().count(), 6)\n\n        query = (U2\n                 .select(U2.username, fn.COUNT(T2.id).alias('ct'))\n                 .join(T2, JOIN.LEFT_OUTER)\n                 .group_by(U2.username)\n                 .order_by(U2.username))\n        self.assertEqual([(u.username, u.ct) for u in query], [\n            ('u1', 3),\n            ('u3', 3)])\n\n\nclass M1(TestModel):\n    name = CharField(primary_key=True)\n    m2 = DeferredForeignKey('M2', deferrable='INITIALLY DEFERRED',\n                            on_delete='CASCADE')\n\nclass M2(TestModel):\n    name = CharField(primary_key=True)\n    m1 = ForeignKeyField(M1, deferrable='INITIALLY DEFERRED',\n                         on_delete='CASCADE')\n\n\n@skip_if(IS_MYSQL)\n@skip_if(IS_CRDB, 'crdb does not support deferred foreign-key constraints')\nclass TestDeferredForeignKey(ModelTestCase):\n    requires = [M1, M2]\n\n    def test_deferred_foreign_key(self):\n        with self.database.atomic():\n            m1 = M1.create(name='m1', m2='m2')\n            m2 = M2.create(name='m2', m1='m1')\n\n        m1_db = M1.get(M1.name == 'm1')\n        self.assertEqual(m1_db.m2.name, 'm2')\n\n        m2_db = M2.get(M2.name == 'm2')\n        self.assertEqual(m2_db.m1.name, 'm1')\n\n\nclass TestDeferredForeignKeyResolution(ModelTestCase):\n    def test_unresolved_deferred_fk(self):\n        class Photo(Model):\n            album = DeferredForeignKey('Album', column_name='id_album')\n            class Meta:\n                database = get_in_memory_db()\n        self.assertSQL(Photo.select(), (\n            'SELECT \"t1\".\"id\", \"t1\".\"id_album\" FROM \"photo\" AS \"t1\"'), [])\n\n    def test_deferred_foreign_key_resolution(self):\n        class Base(Model):\n            class Meta:\n                database = get_in_memory_db()\n\n        class Photo(Base):\n            album = DeferredForeignKey('Album', column_name='id_album',\n                                       null=False, backref='pictures')\n            alt_album = DeferredForeignKey('Album', column_name='id_Alt_album',\n                                           field='alt_id', backref='alt_pix',\n                                           null=True)\n\n        class Album(Base):\n            name = TextField()\n            alt_id = IntegerField(column_name='_Alt_id')\n\n        self.assertTrue(Photo.album.rel_model is Album)\n        self.assertTrue(Photo.album.rel_field is Album.id)\n        self.assertEqual(Photo.album.column_name, 'id_album')\n        self.assertFalse(Photo.album.null)\n\n        self.assertTrue(Photo.alt_album.rel_model is Album)\n        self.assertTrue(Photo.alt_album.rel_field is Album.alt_id)\n        self.assertEqual(Photo.alt_album.column_name, 'id_Alt_album')\n        self.assertTrue(Photo.alt_album.null)\n\n        self.assertSQL(Photo._schema._create_table(), (\n            'CREATE TABLE IF NOT EXISTS \"photo\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"id_album\" INTEGER NOT NULL, '\n            '\"id_Alt_album\" INTEGER)'), [])\n\n        self.assertSQL(Photo._schema._create_foreign_key(Photo.album), (\n            'ALTER TABLE \"photo\" ADD CONSTRAINT \"fk_photo_id_album_refs_album\"'\n            ' FOREIGN KEY (\"id_album\") REFERENCES \"album\" (\"id\")'))\n        self.assertSQL(Photo._schema._create_foreign_key(Photo.alt_album), (\n            'ALTER TABLE \"photo\" ADD CONSTRAINT '\n            '\"fk_photo_id_Alt_album_refs_album\"'\n            ' FOREIGN KEY (\"id_Alt_album\") REFERENCES \"album\" (\"_Alt_id\")'))\n\n        self.assertSQL(Photo.select(), (\n            'SELECT \"t1\".\"id\", \"t1\".\"id_album\", \"t1\".\"id_Alt_album\" '\n            'FROM \"photo\" AS \"t1\"'), [])\n\n        a = Album(id=3, alt_id=4)\n        self.assertSQL(a.pictures, (\n            'SELECT \"t1\".\"id\", \"t1\".\"id_album\", \"t1\".\"id_Alt_album\" '\n            'FROM \"photo\" AS \"t1\" WHERE (\"t1\".\"id_album\" = ?)'), [3])\n        self.assertSQL(a.alt_pix, (\n            'SELECT \"t1\".\"id\", \"t1\".\"id_album\", \"t1\".\"id_Alt_album\" '\n            'FROM \"photo\" AS \"t1\" WHERE (\"t1\".\"id_Alt_album\" = ?)'), [4])\n\n\nclass Composite(TestModel):\n    first = CharField()\n    last = CharField()\n    data = TextField()\n\n    class Meta:\n        primary_key = CompositeKey('first', 'last')\n\n\nclass TestCompositePrimaryKeyField(ModelTestCase):\n    requires = [Composite]\n\n    def test_composite_primary_key(self):\n        pass\n\n\nclass TestFieldFunction(ModelTestCase):\n    requires = [PhoneBook]\n\n    def setUp(self):\n        super(TestFieldFunction, self).setUp()\n        names = ('huey', 'mickey', 'zaizee', 'beanie', 'scout', 'hallee')\n        for name in names:\n            PhoneBook.create(name=name)\n\n    def _test_field_function(self, PB):\n        query = (PB\n                 .select()\n                 .where(PB.name.first_char() == 'h')\n                 .order_by(PB.name))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" '\n            'FROM \"phone_book\" AS \"t1\" '\n            'WHERE (SUBSTR(\"t1\".\"name\", ?, ?) = ?) '\n            'ORDER BY \"t1\".\"name\"'), [1, 1, 'h'])\n\n        self.assertEqual([pb.name for pb in query], ['hallee', 'huey'])\n\n    def test_field_function(self):\n        self._test_field_function(PhoneBook)\n\n    def test_field_function_alias(self):\n        self._test_field_function(PhoneBook.alias())\n\n\nclass IPModel(TestModel):\n    ip = IPField()\n    ip_null = IPField(null=True)\n\n\nclass TestIPField(ModelTestCase):\n    requires = [IPModel]\n\n    def test_ip_field(self):\n        ips = ('0.0.0.0', '255.255.255.255', '192.168.1.1')\n        for ip in ips:\n            i = IPModel.create(ip=ip)\n            i_db = IPModel.get(ip=ip)\n            self.assertEqual(i_db.ip, ip)\n            self.assertEqual(i_db.ip_null, None)\n\n\nclass TestBitFields(ModelTestCase):\n    requires = [Bits]\n\n    def test_bit_field_update(self):\n        def assertFlags(expected):\n            query = Bits.select().order_by(Bits.id)\n            self.assertEqual([b.flags for b in query], expected)\n\n        # Bits - flags (1=sticky, 2=favorite, 4=minimized)\n        for i in range(1, 5):\n            Bits.create(flags=i)\n\n        q = Bits.select((~Bits.flags & 2).alias('bn')).order_by(Bits.id)\n        self.assertEqual([b.bn for b in q], [2, 0, 0, 2])\n\n        q = Bits.select().where((Bits.flags & 2) != 0).order_by(Bits.id)\n        self.assertEqual([b.flags for b in q], [2, 3])\n\n        Bits.update(flags=Bits.flags & ~2).execute()\n        assertFlags([1, 0, 1, 4])\n\n        Bits.update(flags=Bits.flags | 2).execute()\n        assertFlags([3, 2, 3, 6])\n\n        Bits.update(flags=Bits.is_favorite.clear()).execute()\n        assertFlags([1, 0, 1, 4])\n\n        Bits.update(flags=Bits.is_favorite.set()).execute()\n        assertFlags([3, 2, 3, 6])\n\n        # Clear multiple bits in one operation.\n        Bits.update(flags=Bits.flags & ~(1 | 4)).execute()\n        assertFlags([2, 2, 2, 2])\n\n    def test_bit_field_auto_flag(self):\n        class Bits2(TestModel):\n            flags = BitField()\n\n            f1 = flags.flag()  # Automatically gets 1.\n            f2 = flags.flag()  # 2\n            f4 = flags.flag()  # 4\n            f16 = flags.flag(16)\n            f32 = flags.flag()  # 32\n\n        b = Bits2()\n        self.assertEqual(b.flags, 0)\n\n        b.f1 = True\n        self.assertEqual(b.flags, 1)\n        b.f4 = True\n        self.assertEqual(b.flags, 5)\n\n        b.f32 = True\n        self.assertEqual(b.flags, 37)\n\n    def test_bit_field_instance_flags(self):\n        b = Bits()\n        self.assertEqual(b.flags, 0)\n        self.assertFalse(b.is_sticky)\n        self.assertFalse(b.is_favorite)\n        self.assertFalse(b.is_minimized)\n\n        b.is_sticky = True\n        b.is_minimized = True\n        self.assertEqual(b.flags, 5)  # 1 | 4\n\n        self.assertTrue(b.is_sticky)\n        self.assertFalse(b.is_favorite)\n        self.assertTrue(b.is_minimized)\n\n        b.flags = 3\n        self.assertTrue(b.is_sticky)\n        self.assertTrue(b.is_favorite)\n        self.assertFalse(b.is_minimized)\n\n    def test_bit_field(self):\n        b1 = Bits.create(flags=1)\n        b2 = Bits.create(flags=2)\n        b3 = Bits.create(flags=3)\n\n        query = Bits.select().where(Bits.is_sticky).order_by(Bits.id)\n        self.assertEqual([x.id for x in query], [b1.id, b3.id])\n\n        query = Bits.select().where(Bits.is_favorite).order_by(Bits.id)\n        self.assertEqual([x.id for x in query], [b2.id, b3.id])\n\n        query = Bits.select().where(~Bits.is_favorite).order_by(Bits.id)\n        self.assertEqual([x.id for x in query], [b1.id])\n\n        # \"&\" operator does bitwise and for BitField.\n        query = Bits.select().where((Bits.flags & 1) == 1).order_by(Bits.id)\n        self.assertEqual([x.id for x in query], [b1.id, b3.id])\n\n        # Test combining multiple bit expressions.\n        query = Bits.select().where(Bits.is_sticky & Bits.is_favorite)\n        self.assertEqual([x.id for x in query], [b3.id])\n\n        query = Bits.select().where(Bits.is_sticky & ~Bits.is_favorite)\n        self.assertEqual([x.id for x in query], [b1.id])\n\n    def test_bigbit_field_instance_data(self):\n        b = Bits()\n        values_to_set = (1, 11, 63, 31, 55, 48, 100, 99)\n        for value in values_to_set:\n            b.data.set_bit(value)\n\n        for i in range(128):\n            self.assertEqual(b.data.is_set(i), i in values_to_set)\n\n        for i in range(128):\n            b.data.clear_bit(i)\n\n        buf = bytes(b.data._buffer)\n        self.assertEqual(len(buf), 16)\n\n        self.assertEqual(bytes(buf), b'\\x00' * 16)\n\n    def test_bigbit_zero_idx(self):\n        b = Bits()\n        b.data.set_bit(0)\n        self.assertTrue(b.data.is_set(0))\n        b.data.clear_bit(0)\n        self.assertFalse(b.data.is_set(0))\n\n        # Out-of-bounds returns False and does not extend data.\n        self.assertFalse(b.data.is_set(1000))\n        self.assertTrue(len(b.data), 1)\n\n    def test_bigbit_item_methods(self):\n        b = Bits()\n        idxs = [0, 1, 4, 7, 8, 15, 16, 31, 32, 63]\n        for i in idxs:\n            b.data[i] = True\n        for i in range(64):\n            self.assertEqual(b.data[i], i in idxs)\n\n        data = list(b.data)\n        self.assertEqual(data, [1 if i in idxs else 0 for i in range(64)])\n\n        for i in range(64):\n            del b.data[i]\n        self.assertEqual(len(b.data), 8)\n        self.assertEqual(b.data._buffer, b'\\x00' * 8)\n\n    def test_bigbit_set_clear(self):\n        b = Bits()\n        b.data = b'\\x01'\n        for i in range(8):\n            self.assertEqual(b.data[i], i == 0)\n\n        b.data.clear()\n        self.assertEqual(len(b.data), 0)\n\n    def test_bigbit_field(self):\n        b = Bits.create()\n        b.data.set_bit(1)\n        b.data.set_bit(3)\n        b.data.set_bit(5)\n        b.save()\n\n        b_db = Bits.get(Bits.id == b.id)\n        for x in range(7):\n            if x % 2 == 1:\n                self.assertTrue(b_db.data.is_set(x))\n            else:\n                self.assertFalse(b_db.data.is_set(x))\n\n    def test_bigbit_field_bitwise(self):\n        b1 = Bits(data=b'\\x11')\n        b2 = Bits(data=b'\\x12')\n        b3 = Bits(data=b'\\x99')\n        self.assertEqual(b1.data & b2.data, b'\\x10')\n        self.assertEqual(b1.data | b2.data, b'\\x13')\n        self.assertEqual(b1.data ^ b2.data, b'\\x03')\n        self.assertEqual(b1.data & b3.data, b'\\x11')\n        self.assertEqual(b1.data | b3.data, b'\\x99')\n        self.assertEqual(b1.data ^ b3.data, b'\\x88')\n\n        b1.data &= b2.data\n        self.assertEqual(b1.data._buffer, b'\\x10')\n\n        b1.data |= b2.data\n        self.assertEqual(b1.data._buffer, b'\\x12')\n\n        b1.data ^= b3.data\n        self.assertEqual(b1.data._buffer, b'\\x8b')\n\n        b1.data = b'\\x11'\n        self.assertEqual(b1.data & b'\\xff\\xff', b'\\x11\\x00')\n        self.assertEqual(b1.data | b'\\xff\\xff', b'\\xff\\xff')\n        self.assertEqual(b1.data ^ b'\\xff\\xff', b'\\xee\\xff')\n\n        b1.data = b'\\x11\\x11'\n        self.assertEqual(b1.data & b'\\xff', b'\\x11\\x00')\n        self.assertEqual(b1.data | b'\\xff', b'\\xff\\x11')\n        self.assertEqual(b1.data ^ b'\\xff', b'\\xee\\x11')\n\n    def test_bigbit_field_bulk_create(self):\n        b1, b2, b3 = Bits(), Bits(), Bits()\n        b1.data.set_bit(1)\n        b2.data.set_bit(2)\n        b3.data.set_bit(3)\n        Bits.bulk_create([b1, b2, b3])\n        self.assertEqual(len(Bits), 3)\n        for b in Bits.select():\n            self.assertEqual(sum(1 if b.data.is_set(i) else 0\n                                 for i in (1, 2, 3)), 1)\n\n    def test_bigbit_field_bulk_update(self):\n        b1, b2, b3 = Bits.create(), Bits.create(), Bits.create()\n\n        b1.data.set_bit(11)\n        b2.data.set_bit(12)\n        b3.data.set_bit(13)\n        Bits.bulk_update([b1, b2, b3], fields=[Bits.data])\n\n        mapping = {b1.id: 11, b2.id: 12, b3.id: 13}\n        for b in Bits.select():\n            bit = mapping[b.id]\n            self.assertTrue(b.data.is_set(bit))\n\n\nclass BlobModel(TestModel):\n    data = BlobField()\n\n\nclass TestBlobField(ModelTestCase):\n    requires = [BlobModel]\n\n    def test_blob_field(self):\n        b = BlobModel.create(data=b'\\xff\\x01')\n        b_db = BlobModel.get(BlobModel.data == b'\\xff\\x01')\n        self.assertEqual(b.id, b_db.id)\n\n        data = b_db.data\n        if isinstance(data, memoryview):\n            data = data.tobytes()\n        elif not isinstance(data, bytes):\n            data = bytes(data)\n        self.assertEqual(data, b'\\xff\\x01')\n\n    def test_blob_on_proxy(self):\n        db = Proxy()\n        class NewBlobModel(Model):\n            data = BlobField()\n            class Meta:\n                database = db\n\n        db_obj = SqliteDatabase(':memory:')\n        db.initialize(db_obj)\n        self.assertTrue(NewBlobModel.data._constructor is sqlite3.Binary)\n\n    def test_blob_db_hook(self):\n        sentinel = object()\n\n        class FakeDatabase(Database):\n            def get_binary_type(self):\n                return sentinel\n\n        class B(Model):\n            b1 = BlobField()\n            b2 = BlobField()\n\n        B._meta.set_database(FakeDatabase(None))\n        self.assertTrue(B.b1._constructor is sentinel)\n        self.assertTrue(B.b2._constructor is sentinel)\n\n        alt_db = SqliteDatabase(':memory:')\n        with alt_db.bind_ctx([B]):\n            # The constructor has been changed.\n            self.assertTrue(B.b1._constructor is sqlite3.Binary)\n            self.assertTrue(B.b2._constructor is sqlite3.Binary)\n\n        # The constructor has been restored.\n        self.assertTrue(B.b1._constructor is sentinel)\n        self.assertTrue(B.b2._constructor is sentinel)\n\n\nclass BigModel(TestModel):\n    pk = BigAutoField()\n    data = TextField()\n\n\nclass TestBigAutoField(ModelTestCase):\n    requires = [BigModel]\n\n    def test_big_auto_field(self):\n        b1 = BigModel.create(data='b1')\n        b2 = BigModel.create(data='b2')\n\n        b1_db = BigModel.get(BigModel.pk == b1.pk)\n        b2_db = BigModel.get(BigModel.pk == b2.pk)\n\n        self.assertTrue(b1_db.pk < b2_db.pk)\n        self.assertTrue(b1_db.data, 'b1')\n        self.assertTrue(b2_db.data, 'b2')\n\n\nclass Item(TestModel):\n    price = IntegerField()\n    multiplier = FloatField(default=1.)\n\n\nclass Bare(TestModel):\n    key = BareField()\n    value = BareField(adapt=int, null=True)\n\n\nclass TestFieldValueHandling(ModelTestCase):\n    requires = [Item]\n\n    @skip_if(IS_CRDB, 'crdb requires cast to multiply int and float')\n    def test_int_float_multi(self):\n        i = Item.create(price=10, multiplier=0.75)\n\n        query = (Item\n                 .select(Item, (Item.price * Item.multiplier).alias('total'))\n                 .where(Item.id == i.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"price\", \"t1\".\"multiplier\", '\n            '(\"t1\".\"price\" * \"t1\".\"multiplier\") AS \"total\" '\n            'FROM \"item\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" = ?)'), [i.id])\n\n        i_db = query.get()\n        self.assertEqual(i_db.price, 10)\n        self.assertEqual(i_db.multiplier, .75)\n        self.assertEqual(i_db.total, 7.5)\n\n        # By default, Peewee will use the Price field (integer) converter to\n        # coerce the value of it's right-hand operand (converting to 0).\n        query = (Item\n                 .select(Item, (Item.price * 0.75).alias('total'))\n                 .where(Item.id == i.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"price\", \"t1\".\"multiplier\", '\n            '(\"t1\".\"price\" * ?) AS \"total\" '\n            'FROM \"item\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" = ?)'), [0, i.id])\n\n        # We can explicitly pass \"False\" and the value will not be converted.\n        exp = Item.price * Value(0.75, False)\n        query = (Item\n                 .select(Item, exp.alias('total'))\n                 .where(Item.id == i.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"price\", \"t1\".\"multiplier\", '\n            '(\"t1\".\"price\" * ?) AS \"total\" '\n            'FROM \"item\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" = ?)'), [0.75, i.id])\n\n        i_db = query.get()\n        self.assertEqual(i_db.price, 10)\n        self.assertEqual(i_db.multiplier, .75)\n        self.assertEqual(i_db.total, 7.5)\n\n    def test_explicit_cast(self):\n        prices = ((10, 1.1), (5, .5))\n        for price, multiplier in prices:\n            Item.create(price=price, multiplier=multiplier)\n\n        text = 'CHAR' if IS_MYSQL else 'TEXT'\n\n        query = (Item\n                 .select(Item.price.cast(text).alias('price_text'),\n                         Item.multiplier.cast(text).alias('multiplier_text'))\n                 .order_by(Item.id)\n                 .dicts())\n        self.assertEqual(list(query), [\n            {'price_text': '10', 'multiplier_text': '1.1'},\n            {'price_text': '5', 'multiplier_text': '0.5'},\n        ])\n\n        item = (Item\n                .select(Item.price.cast(text).alias('price'),\n                        Item.multiplier.cast(text).alias('multiplier'))\n                .where(Item.price == 10)\n                .get())\n        self.assertEqual(item.price, '10')\n        self.assertEqual(item.multiplier, '1.1')\n\n    @requires_sqlite\n    @requires_models(Bare)\n    def test_bare_model_adapt(self):\n        b1 = Bare.create(key='k1', value=1)\n        b2 = Bare.create(key='k2', value='2')\n        b3 = Bare.create(key='k3', value=None)\n\n        b1_db = Bare.get(Bare.id == b1.id)\n        self.assertEqual(b1_db.key, 'k1')\n        self.assertEqual(b1_db.value, 1)\n\n        b2_db = Bare.get(Bare.id == b2.id)\n        self.assertEqual(b2_db.key, 'k2')\n        self.assertEqual(b2_db.value, 2)\n\n        b3_db = Bare.get(Bare.id == b3.id)\n        self.assertEqual(b3_db.key, 'k3')\n        self.assertTrue(b3_db.value is None)\n\n\nclass UUIDModel(TestModel):\n    data = UUIDField(null=True)\n    bdata = BinaryUUIDField(null=True)\n\n\nclass TestUUIDField(ModelTestCase):\n    requires = [UUIDModel]\n\n    def test_uuid_field(self):\n        uu = uuid.uuid4()\n        u = UUIDModel.create(data=uu)\n\n        u_db = UUIDModel.get(UUIDModel.id == u.id)\n        self.assertEqual(u_db.data, uu)\n        self.assertTrue(u_db.bdata is None)\n\n        u_db2 = UUIDModel.get(UUIDModel.data == uu)\n        self.assertEqual(u_db2.id, u.id)\n\n        # Verify we can use hex string.\n        uu = uuid.uuid4()\n        u = UUIDModel.create(data=uu.hex)\n        u_db = UUIDModel.get(UUIDModel.data == uu.hex)\n        self.assertEqual(u.id, u_db.id)\n        self.assertEqual(u_db.data, uu)\n\n        # Verify we can use raw binary representation.\n        uu = uuid.uuid4()\n        u = UUIDModel.create(data=uu.bytes)\n        u_db = UUIDModel.get(UUIDModel.data == uu.bytes)\n        self.assertEqual(u.id, u_db.id)\n        self.assertEqual(u_db.data, uu)\n\n    def test_binary_uuid_field(self):\n        uu = uuid.uuid4()\n        u = UUIDModel.create(bdata=uu)\n\n        u_db = UUIDModel.get(UUIDModel.id == u.id)\n        self.assertEqual(u_db.bdata, uu)\n        self.assertTrue(u_db.data is None)\n\n        u_db2 = UUIDModel.get(UUIDModel.bdata == uu)\n        self.assertEqual(u_db2.id, u.id)\n\n        # Verify we can use hex string.\n        uu = uuid.uuid4()\n        u = UUIDModel.create(bdata=uu.hex)\n        u_db = UUIDModel.get(UUIDModel.bdata == uu.hex)\n        self.assertEqual(u.id, u_db.id)\n        self.assertEqual(u_db.bdata, uu)\n\n        # Verify we can use raw binary representation.\n        uu = uuid.uuid4()\n        u = UUIDModel.create(bdata=uu.bytes)\n        u_db = UUIDModel.get(UUIDModel.bdata == uu.bytes)\n        self.assertEqual(u.id, u_db.id)\n        self.assertEqual(u_db.bdata, uu)\n\n\nclass UU1(TestModel):\n    id = UUIDField(default=uuid.uuid4, primary_key=True)\n    name = TextField()\n\nclass UU2(TestModel):\n    id = UUIDField(default=uuid.uuid4, primary_key=True)\n    u1 = ForeignKeyField(UU1)\n    name = TextField()\n\n\nclass TestForeignKeyUUIDField(ModelTestCase):\n    requires = [UU1, UU2]\n\n    def test_bulk_insert(self):\n        # Create three UU1 instances.\n        UU1.insert_many([{UU1.name: name} for name in 'abc'],\n                       fields=[UU1.id, UU1.name]).execute()\n        ua, ub, uc = UU1.select().order_by(UU1.name)\n\n        # Create several UU2 instances.\n        data = (\n            ('a1', ua),\n            ('b1', ub),\n            ('b2', ub),\n            ('c1', uc))\n        iq = UU2.insert_many([{UU2.name: name, UU2.u1: u} for name, u in data],\n                             fields=[UU2.id, UU2.name, UU2.u1])\n        iq.execute()\n\n        query = UU2.select().order_by(UU2.name)\n        for (name, u1), u2 in zip(data, query):\n            self.assertEqual(u2.name, name)\n            self.assertEqual(u2.u1.id, u1.id)\n\n\nclass TSModel(TestModel):\n    ts_s = TimestampField()\n    ts_us = TimestampField(resolution=10 ** 6)\n    ts_ms = TimestampField(resolution=3)  # Milliseconds.\n    ts_u = TimestampField(null=True, utc=True)\n\n\nclass TSR(TestModel):\n    ts_0 = TimestampField(resolution=0)\n    ts_1 = TimestampField(resolution=1)\n    ts_10 = TimestampField(resolution=10)\n    ts_2 = TimestampField(resolution=2)\n\n\nclass TestTimestampField(ModelTestCase):\n    requires = [TSModel]\n\n    @requires_models(TSR)\n    def test_timestamp_field_resolutions(self):\n        dt = datetime.datetime(2018, 3, 1, 3, 3, 7).replace(microsecond=123456)\n        ts = TSR.create(ts_0=dt, ts_1=dt, ts_10=dt, ts_2=dt)\n        ts_db = TSR[ts.id]\n\n        # Zero and one are both treated as \"seconds\" resolution.\n        self.assertEqual(ts_db.ts_0, dt.replace(microsecond=0))\n        self.assertEqual(ts_db.ts_1, dt.replace(microsecond=0))\n        self.assertEqual(ts_db.ts_10, dt.replace(microsecond=100000))\n        self.assertEqual(ts_db.ts_2, dt.replace(microsecond=120000))\n\n    def test_timestamp_field(self):\n        dt = datetime.datetime(2018, 3, 1, 3, 3, 7)\n        dt = dt.replace(microsecond=31337)  # us=031_337, ms=031.\n        ts = TSModel.create(ts_s=dt, ts_us=dt, ts_ms=dt, ts_u=dt)\n        ts_db = TSModel.get(TSModel.id == ts.id)\n        self.assertEqual(ts_db.ts_s, dt.replace(microsecond=0))\n        self.assertEqual(ts_db.ts_ms, dt.replace(microsecond=31000))\n        self.assertEqual(ts_db.ts_us, dt)\n        self.assertEqual(ts_db.ts_u, dt.replace(microsecond=0))\n\n        self.assertEqual(TSModel.get(TSModel.ts_s == dt).id, ts.id)\n        self.assertEqual(TSModel.get(TSModel.ts_ms == dt).id, ts.id)\n        self.assertEqual(TSModel.get(TSModel.ts_us == dt).id, ts.id)\n        self.assertEqual(TSModel.get(TSModel.ts_u == dt).id, ts.id)\n\n    def test_timestamp_field_math(self):\n        dt = datetime.datetime(2019, 1, 2, 3, 4, 5, 31337)\n        ts = TSModel.create(ts_s=dt, ts_us=dt, ts_ms=dt)\n\n        # Although these fields use different scales for storing the\n        # timestamps, adding \"1\" has the effect of adding a single second -\n        # the value will be multiplied by the correct scale via the converter.\n        TSModel.update(\n            ts_s=TSModel.ts_s + 1,\n            ts_us=TSModel.ts_us + 1,\n            ts_ms=TSModel.ts_ms + 1).execute()\n\n        ts_db = TSModel.get(TSModel.id == ts.id)\n        dt2 = dt + datetime.timedelta(seconds=1)\n        self.assertEqual(ts_db.ts_s, dt2.replace(microsecond=0))\n        self.assertEqual(ts_db.ts_us, dt2)\n        self.assertEqual(ts_db.ts_ms, dt2.replace(microsecond=31000))\n\n    def test_timestamp_field_value_as_ts(self):\n        dt = datetime.datetime(2018, 3, 1, 3, 3, 7, 31337)\n        unix_ts = time.mktime(dt.timetuple()) + 0.031337\n        ts = TSModel.create(ts_s=unix_ts, ts_us=unix_ts, ts_ms=unix_ts,\n                            ts_u=unix_ts)\n\n        # Fetch from the DB and validate the values were stored correctly.\n        ts_db = TSModel[ts.id]\n        self.assertEqual(ts_db.ts_s, dt.replace(microsecond=0))\n        self.assertEqual(ts_db.ts_ms, dt.replace(microsecond=31000))\n        self.assertEqual(ts_db.ts_us, dt)\n\n        utc_dt = TimestampField().local_to_utc(dt)\n        self.assertEqual(ts_db.ts_u, utc_dt)\n\n        # Verify we can query using a timestamp.\n        self.assertEqual(TSModel.get(TSModel.ts_s == unix_ts).id, ts.id)\n        self.assertEqual(TSModel.get(TSModel.ts_ms == unix_ts).id, ts.id)\n        self.assertEqual(TSModel.get(TSModel.ts_us == unix_ts).id, ts.id)\n        self.assertEqual(TSModel.get(TSModel.ts_u == unix_ts).id, ts.id)\n\n    def test_timestamp_utc_vs_localtime(self):\n        local_field = TimestampField()\n        utc_field = TimestampField(utc=True)\n\n        dt = datetime.datetime(2019, 1, 1, 12)\n        unix_ts = int(local_field.get_timestamp(dt))\n        utc_ts = int(utc_field.get_timestamp(dt))\n\n        # Local timestamp is unmodified. Verify that when utc=True, the\n        # timestamp is converted from local time to UTC.\n        self.assertEqual(local_field.db_value(dt), unix_ts)\n        self.assertEqual(utc_field.db_value(dt), utc_ts)\n\n        self.assertEqual(local_field.python_value(unix_ts), dt)\n        self.assertEqual(utc_field.python_value(utc_ts), dt)\n\n        # Convert back-and-forth several times.\n        dbv, pyv = local_field.db_value, local_field.python_value\n        self.assertEqual(pyv(dbv(pyv(dbv(dt)))), dt)\n\n        dbv, pyv = utc_field.db_value, utc_field.python_value\n        self.assertEqual(pyv(dbv(pyv(dbv(dt)))), dt)\n\n    def test_timestamp_field_parts(self):\n        dt = datetime.datetime(2019, 1, 2, 3, 4, 5)\n        dt_utc = TimestampField().local_to_utc(dt)\n        ts = TSModel.create(ts_s=dt, ts_us=dt, ts_ms=dt, ts_u=dt_utc)\n\n        fields = (TSModel.ts_s, TSModel.ts_us, TSModel.ts_ms, TSModel.ts_u)\n        attrs = ('year', 'month', 'day', 'hour', 'minute', 'second')\n        selection = []\n        for field in fields:\n            for attr in attrs:\n                selection.append(getattr(field, attr))\n\n        row = TSModel.select(*selection).tuples()[0]\n\n        # First ensure that all 3 fields are returning the same data.\n        ts_s, ts_us, ts_ms, ts_u = row[:6], row[6:12], row[12:18], row[18:]\n        self.assertEqual(ts_s, ts_us)\n        self.assertEqual(ts_s, ts_ms)\n        self.assertEqual(ts_s, ts_u)\n\n        # Now validate that the data is correct. We will receive the data back\n        # as a UTC unix timestamp, however!\n        y, m, d, H, M, S = ts_s\n        self.assertEqual(y, 2019)\n        self.assertEqual(m, 1)\n        self.assertEqual(d, dt_utc.day)\n        self.assertEqual(H, dt_utc.hour)\n        self.assertEqual(M, 4)\n        self.assertEqual(S, 5)\n\n    def test_timestamp_field_from_ts(self):\n        dt = datetime.datetime(2019, 1, 2, 3, 4, 5)\n        dt_utc = TimestampField().local_to_utc(dt)\n\n        ts = TSModel.create(ts_s=dt, ts_us=dt, ts_ms=dt, ts_u=dt_utc)\n        query = TSModel.select(\n            TSModel.ts_s.from_timestamp().alias('dt_s'),\n            TSModel.ts_us.from_timestamp().alias('dt_us'),\n            TSModel.ts_ms.from_timestamp().alias('dt_ms'),\n            TSModel.ts_u.from_timestamp().alias('dt_u'))\n\n        # Get row and unpack into variables corresponding to the fields.\n        row = query.tuples()[0]\n        dt_s, dt_us, dt_ms, dt_u = row\n\n        # Ensure the timestamp values for all 4 fields are the same.\n        self.assertEqual(dt_s, dt_us)\n        self.assertEqual(dt_s, dt_ms)\n        self.assertEqual(dt_s, dt_u)\n        if IS_SQLITE:\n            expected = dt_utc.strftime('%Y-%m-%d %H:%M:%S')\n            self.assertEqual(dt_s, expected)\n        elif IS_POSTGRESQL or IS_CRDB:\n            # Postgres returns an aware UTC datetime. Strip this to compare\n            # against our naive UTC datetime.\n            self.assertEqual(dt_s.replace(tzinfo=None), dt_utc)\n\n    def test_invalid_resolution(self):\n        self.assertRaises(ValueError, TimestampField, resolution=7)\n        self.assertRaises(ValueError, TimestampField, resolution=20)\n        self.assertRaises(ValueError, TimestampField, resolution=10**7)\n\n\nclass ListField(TextField):\n    def db_value(self, value):\n        return ','.join(value) if value else ''\n\n    def python_value(self, value):\n        return value.split(',') if value else []\n\n\nclass Todo(TestModel):\n    content = TextField()\n    tags = ListField()\n\n\nclass TestCustomField(ModelTestCase):\n    requires = [Todo]\n\n    def test_custom_field(self):\n        t1 = Todo.create(content='t1', tags=['t1-a', 't1-b'])\n        t2 = Todo.create(content='t2', tags=[])\n\n        t1_db = Todo.get(Todo.id == t1.id)\n        self.assertEqual(t1_db.tags, ['t1-a', 't1-b'])\n\n        t2_db = Todo.get(Todo.id == t2.id)\n        self.assertEqual(t2_db.tags, [])\n\n        t1_db = Todo.get(Todo.tags == AsIs(['t1-a', 't1-b']))\n        self.assertEqual(t1_db.id, t1.id)\n\n        t2_db = Todo.get(Todo.tags == AsIs([]))\n        self.assertEqual(t2_db.id, t2.id)\n\n\nclass UpperField(TextField):\n    def db_value(self, value):\n        return fn.UPPER(value)\n\n\nclass UpperModel(TestModel):\n    name = UpperField()\n\n\nclass TestSQLFunctionDBValue(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [UpperModel]\n\n    def test_sql_function_db_value(self):\n        # Verify that the db function is applied as part of an INSERT.\n        um = UpperModel.create(name='huey')\n        um_db = UpperModel.get(UpperModel.id == um.id)\n        self.assertEqual(um_db.name, 'HUEY')\n\n        # Verify that the db function is applied as part of an UPDATE.\n        um_db.name = 'zaizee'\n        um_db.save()\n\n        # Ensure that the name was updated correctly.\n        um_db2 = UpperModel.get(UpperModel.id == um.id)\n        self.assertEqual(um_db2.name, 'ZAIZEE')\n\n        # Verify that the db function is applied in a WHERE expression.\n        um_db3 = UpperModel.get(UpperModel.name == 'zaiZee')\n        self.assertEqual(um_db3.id, um.id)\n\n        # If we nest the field in a function, the conversion is not applied.\n        expr = fn.SUBSTR(UpperModel.name, 1, 1) == 'z'\n        self.assertRaises(UpperModel.DoesNotExist, UpperModel.get, expr)\n\n\nclass Schedule(TestModel):\n    interval = IntegerField()\n\nclass Task(TestModel):\n    schedule = ForeignKeyField(Schedule)\n    name = TextField()\n    last_run = DateTimeField()\n\n\nclass TestDateTimeMath(ModelTestCase):\n    offset_to_names = (\n        (-10, ()),\n        (5, ('s1',)),\n        (10, ('s1', 's10')),\n        (11, ('s1', 's10')),\n        (60, ('s1', 's10', 's60')),\n        (61, ('s1', 's10', 's60')))\n    requires = [Schedule, Task]\n\n    def setUp(self):\n        super(TestDateTimeMath, self).setUp()\n        with self.database.atomic():\n            s1 = Schedule.create(interval=1)\n            s10 = Schedule.create(interval=10)\n            s60 = Schedule.create(interval=60)\n\n            self.dt = datetime.datetime(2019, 1, 1, 12)\n            for s, n in ((s1, 's1'), (s10, 's10'), (s60, 's60')):\n                Task.create(schedule=s, name=n, last_run=self.dt)\n\n    def _do_test_date_time_math(self, next_occurrence_expression):\n        for offset, names in self.offset_to_names:\n            dt = Value(self.dt + datetime.timedelta(seconds=offset))\n            query = (Task\n                     .select(Task, Schedule)\n                     .join(Schedule)\n                     .where(dt >= next_occurrence_expression)\n                     .order_by(Schedule.interval))\n            tnames = [task.name for task in query]\n            self.assertEqual(list(names), tnames)\n\n    @requires_pglike\n    def test_date_time_math_pg(self):\n        second = SQL(\"INTERVAL '1 second'\")\n        next_occurrence = Task.last_run + (Schedule.interval * second)\n        self._do_test_date_time_math(next_occurrence)\n\n    @requires_sqlite\n    def test_date_time_math_sqlite(self):\n        # Convert to a timestamp, add the scheduled seconds, then convert back\n        # to a datetime string for comparison with the last occurrence.\n        next_ts = Task.last_run.to_timestamp() + Schedule.interval\n        next_occurrence = fn.datetime(next_ts, 'unixepoch')\n        self._do_test_date_time_math(next_occurrence)\n\n    @requires_mysql\n    def test_date_time_math_mysql(self):\n        nl = NodeList((SQL('INTERVAL'), Schedule.interval, SQL('SECOND')))\n        next_occurrence = fn.date_add(Task.last_run, nl)\n        self._do_test_date_time_math(next_occurrence)\n\n\nclass NQ(TestModel):\n    name = TextField()\n\nclass NQItem(TestModel):\n    nq = ForeignKeyField(NQ, backref='items')\n    nq_null = ForeignKeyField(NQ, backref='null_items', null=True)\n    nq_lazy = ForeignKeyField(NQ, lazy_load=False, backref='lazy_items')\n    nq_lazy_null = ForeignKeyField(NQ, lazy_load=False,\n                                   backref='lazy_null_items', null=True)\n\n\nclass TestForeignKeyLazyLoad(ModelTestCase):\n    requires = [NQ, NQItem]\n\n    def setUp(self):\n        super(TestForeignKeyLazyLoad, self).setUp()\n        with self.database.atomic():\n            a1, a2, a3, a4 = [NQ.create(name='a%s' % i) for i in range(1, 5)]\n            ai = NQItem.create(nq=a1, nq_null=a2, nq_lazy=a3, nq_lazy_null=a4)\n\n            b = NQ.create(name='b')\n            bi = NQItem.create(nq=b, nq_lazy=b)\n\n    def test_doesnotexist_lazy_load(self):\n        n = NQ.create(name='n1')\n        i = NQItem.create(nq=n, nq_null=n, nq_lazy=n, nq_lazy_null=n)\n\n        i_db = NQItem.select(NQItem.id).where(NQItem.nq == n).get()\n        with self.assertQueryCount(0):\n            # Only raise DoesNotExist for non-nullable *and* lazy-load=True.\n            # Otherwise we just return None.\n            self.assertRaises(NQ.DoesNotExist, lambda: i_db.nq)\n            self.assertTrue(i_db.nq_null is None)\n            self.assertTrue(i_db.nq_lazy is None)\n            self.assertTrue(i_db.nq_lazy_null is None)\n\n    def test_foreign_key_lazy_load(self):\n        a1, a2, a3, a4 = (NQ.select()\n                          .where(NQ.name.startswith('a'))\n                          .order_by(NQ.name))\n        b = NQ.get(NQ.name == 'b')\n        ai = NQItem.get(NQItem.nq_id == a1.id)\n        bi = NQItem.get(NQItem.nq_id == b.id)\n\n        # Accessing the lazy foreign-key fields will not result in any queries\n        # being executed.\n        with self.assertQueryCount(0):\n            self.assertEqual(ai.nq_lazy, a3.id)\n            self.assertEqual(ai.nq_lazy_null, a4.id)\n            self.assertEqual(bi.nq_lazy, b.id)\n            self.assertTrue(bi.nq_lazy_null is None)\n            self.assertTrue(bi.nq_null is None)\n\n        # Accessing the regular foreign-key fields uses a query to get the\n        # related model instance.\n        with self.assertQueryCount(2):\n            self.assertEqual(ai.nq.id, a1.id)\n            self.assertEqual(ai.nq_null.id, a2.id)\n\n        with self.assertQueryCount(1):\n            self.assertEqual(bi.nq.id, b.id)\n\n    def test_fk_lazy_load_related_instance(self):\n        nq = NQ(name='b1')\n        nqi = NQItem(nq=nq, nq_null=nq, nq_lazy=nq, nq_lazy_null=nq)\n        nq.save()\n        nqi.save()\n\n        with self.assertQueryCount(1):\n            nqi_db = NQItem.get(NQItem.id == nqi.id)\n            self.assertEqual(nqi_db.nq_lazy, nq.id)\n            self.assertEqual(nqi_db.nq_lazy_null, nq.id)\n\n    def test_fk_lazy_select_related(self):\n        NA, NB, NC, ND = [NQ.alias(a) for a in ('na', 'nb', 'nc', 'nd')]\n        LO = JOIN.LEFT_OUTER\n        query = (NQItem.select(NQItem, NA, NB, NC, ND)\n                 .join_from(NQItem, NA, LO, on=NQItem.nq)\n                 .join_from(NQItem, NB, LO, on=NQItem.nq_null)\n                 .join_from(NQItem, NC, LO, on=NQItem.nq_lazy)\n                 .join_from(NQItem, ND, LO, on=NQItem.nq_lazy_null)\n                 .order_by(NQItem.id))\n\n        # If we explicitly / eagerly select lazy foreign-key models, they\n        # behave just like regular foreign keys.\n        with self.assertQueryCount(1):\n            ai, bi = [ni for ni in query]\n            self.assertEqual(ai.nq.name, 'a1')\n            self.assertEqual(ai.nq_null.name, 'a2')\n            self.assertEqual(ai.nq_lazy.name, 'a3')\n            self.assertEqual(ai.nq_lazy_null.name, 'a4')\n\n            self.assertEqual(bi.nq.name, 'b')\n            self.assertEqual(bi.nq_lazy.name, 'b')\n            self.assertTrue(bi.nq_null is None)\n            self.assertTrue(bi.nq_lazy_null is None)\n\n\nclass SM(TestModel):\n    text_field = TextField()\n    char_field = CharField()\n\n\nclass TestStringFields(ModelTestCase):\n    requires = [SM]\n\n    def test_string_fields(self):\n        bdata = b'b1'\n        udata = b'u1'.decode('utf8')\n\n        sb = SM.create(text_field=bdata, char_field=bdata)\n        su = SM.create(text_field=udata, char_field=udata)\n\n        sb_db = SM.get(SM.id == sb.id)\n        self.assertEqual(sb_db.text_field, 'b1')\n        self.assertEqual(sb_db.char_field, 'b1')\n\n        su_db = SM.get(SM.id == su.id)\n        self.assertEqual(su_db.text_field, 'u1')\n        self.assertEqual(su_db.char_field, 'u1')\n\n        bvals = (b'b1', u'b1')\n        uvals = (b'u1', u'u1')\n\n        for field in (SM.text_field, SM.char_field):\n            for bval in bvals:\n                sb_db = SM.get(field == bval)\n                self.assertEqual(sb.id, sb_db.id)\n\n            for uval in uvals:\n                sb_db = SM.get(field == uval)\n                self.assertEqual(su.id, su_db.id)\n\n\nclass InvalidTypes(TestModel):\n    tfield = TextField()\n    ifield = IntegerField()\n    ffield = FloatField()\n\n\nclass TestSqliteInvalidDataTypes(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [InvalidTypes]\n\n    def test_invalid_data_types(self):\n        it = InvalidTypes.create(tfield=100, ifield='five', ffield='pi')\n        it_db1 = InvalidTypes.get(InvalidTypes.tfield == 100)\n        it_db2 = InvalidTypes.get(InvalidTypes.ifield == 'five')\n        it_db3 = InvalidTypes.get(InvalidTypes.ffield == 'pi')\n        self.assertTrue(it.id == it_db1.id == it_db2.id == it_db3.id)\n\n        self.assertEqual(it_db1.tfield, '100')\n        self.assertEqual(it_db1.ifield, 'five')\n        self.assertEqual(it_db1.ffield, 'pi')\n\n\nclass DblSI(TestModel):\n    df = DoubleField()\n    si = SmallIntegerField()\n\n\nclass TestDoubleSmallInt(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [DblSI]\n\n    def test_double_round_trip(self):\n        DblSI.create(df=3.141592653589793, si=0)\n        obj = DblSI.get()\n        self.assertAlmostEqual(obj.df, 3.141592653589793, places=10)\n\n    def test_small_int_round_trip(self):\n        DblSI.create(df=0, si=32000)\n        DblSI.create(df=0, si=-100)\n        results = (DblSI\n                   .select(DblSI.si)\n                   .order_by(DblSI.si)\n                   .tuples())\n        self.assertEqual(list(results), [(-100,), (32000,)])\n\n    def test_coercion(self):\n        DblSI.create(df=float('inf'), si='42')\n        obj = DblSI.get()\n        self.assertEqual(obj.df, float('inf'))\n        self.assertEqual(obj.si, 42)\n\n        obj = DblSI.create(df=float('-inf'), si='1.23')\n        obj = DblSI.get(DblSI.id == obj.id)\n        self.assertEqual(obj.df, float('-inf'))\n        self.assertEqual(obj.si, 1)\n\n\nclass FC(TestModel):\n    code = FixedCharField(max_length=5)\n    name = CharField()\n\n\nclass TestFixedCharFieldIntegration(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [FC]\n\n    def test_fixed_char_truncates(self):\n        FC.create(code='ABCDEF', name='short')\n\n        fc = FC.get(FC.code == 'ABCDE')\n        self.assertEqual(fc.code, 'ABCDE')\n\n\nclass VF(TestModel):\n    name = TextField()\n    computed = VirtualField(field_class=IntegerField)\n\n\nclass TestVirtualFieldBehavior(BaseTestCase):\n    def test_virtual_field_not_in_columns(self):\n        \"\"\"VirtualField should not appear in the model's SELECT columns.\"\"\"\n        fields = VF._meta.sorted_fields\n        field_names = [f.name for f in fields]\n        self.assertIn('name', field_names)\n        # VirtualField should not be in sorted_fields (it's a MetaField).\n        self.assertNotIn('computed', field_names)\n\n        query = VF.select()\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" FROM \"vf\" AS \"t1\"'))\n\n    def test_virtual_field_db_value(self):\n        vf = VF.computed\n        self.assertEqual(vf.db_value('42'), 42)\n        self.assertEqual(vf.python_value('42'), 42)\n"
  },
  {
    "path": "tests/hybrid.py",
    "content": "from peewee import *\nfrom playhouse.hybrid import *\n\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import get_in_memory_db\nfrom .base import requires_models\n\n\nclass Interval(TestModel):\n    start = IntegerField()\n    end = IntegerField()\n\n    @hybrid_property\n    def length(self):\n        return self.end - self.start\n\n    @hybrid_method\n    def contains(self, point):\n        return (self.start <= point) & (point < self.end)\n\n    @hybrid_property\n    def radius(self):\n        return int(abs(self.length) / 2)\n\n    @radius.expression\n    def radius(cls):\n        return fn.ABS(cls.length) / 2\n\n\nclass Person(TestModel):\n    first = TextField()\n    last = TextField()\n\n    @hybrid_property\n    def full_name(self):\n        return self.first + ' ' + self.last\n\nclass SubPerson(Person):\n    pass\n\n\nclass TestHybridProperties(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Interval, Person]\n\n    def setUp(self):\n        super(TestHybridProperties, self).setUp()\n        intervals = (\n            (1, 5),\n            (2, 6),\n            (3, 5),\n            (2, 5))\n        for start, end in intervals:\n            Interval.create(start=start, end=end)\n\n    def test_hybrid_property(self):\n        query = Interval.select().where(Interval.length == 4)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"start\", \"t1\".\"end\" '\n            'FROM \"interval\" AS \"t1\" '\n            'WHERE ((\"t1\".\"end\" - \"t1\".\"start\") = ?)'), [4])\n\n        results = sorted((i.start, i.end) for i in query)\n        self.assertEqual(results, [(1, 5), (2, 6)])\n\n        query = Interval.select().order_by(Interval.id)\n        self.assertEqual([i.length for i in query], [4, 4, 2, 3])\n\n    def test_hybrid_method(self):\n        query = Interval.select().where(Interval.contains(2))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"start\", \"t1\".\"end\" '\n            'FROM \"interval\" AS \"t1\" '\n            'WHERE ((\"t1\".\"start\" <= ?) AND (\"t1\".\"end\" > ?))'), [2, 2])\n\n        results = sorted((i.start, i.end) for i in query)\n        self.assertEqual(results, [(1, 5), (2, 5), (2, 6)])\n\n        query = Interval.select().order_by(Interval.id)\n        self.assertEqual([i.contains(2) for i in query], [1, 1, 0, 1])\n\n    def test_expression(self):\n        query = Interval.select().where(Interval.radius == 2)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"start\", \"t1\".\"end\" '\n            'FROM \"interval\" AS \"t1\" '\n            'WHERE ((ABS(\"t1\".\"end\" - \"t1\".\"start\") / ?) = ?)'), [2, 2])\n\n        self.assertEqual(sorted((i.start, i.end) for i in query),\n                         [(1, 5), (2, 6)])\n\n        query = Interval.select().order_by(Interval.id)\n        self.assertEqual([i.radius for i in query], [2, 2, 1, 1])\n\n    def test_string_fields(self):\n        huey = Person.create(first='huey', last='cat')\n        zaizee = Person.create(first='zaizee', last='kitten')\n\n        self.assertEqual(huey.full_name, 'huey cat')\n        self.assertEqual(zaizee.full_name, 'zaizee kitten')\n\n        query = Person.select().where(Person.full_name.startswith('huey c'))\n        huey_db = query.get()\n        self.assertEqual(huey_db.id, huey.id)\n\n    def test_hybrid_model_alias(self):\n        Person.create(first='huey', last='cat')\n        PA = Person.alias()\n        query = PA.select(PA.full_name).where(PA.last == 'cat')\n        self.assertSQL(query, (\n            'SELECT ((\"t1\".\"first\" || ?) || \"t1\".\"last\") '\n            'FROM \"person\" AS \"t1\" WHERE (\"t1\".\"last\" = ?)'), [' ', 'cat'])\n        self.assertEqual(query.tuples()[0], ('huey cat',))\n\n    @requires_models(SubPerson)\n    def test_hybrid_subclass_model_alias(self):\n        SubPerson.create(first='huey', last='cat')\n        SA = SubPerson.alias()\n        query = SA.select(SA.full_name).where(SA.last == 'cat')\n        self.assertSQL(query, (\n            'SELECT ((\"t1\".\"first\" || ?) || \"t1\".\"last\") '\n            'FROM \"sub_person\" AS \"t1\" WHERE (\"t1\".\"last\" = ?)'), [' ', 'cat'])\n        self.assertEqual(query.tuples()[0], ('huey cat',))\n\n\nclass Order(TestModel):\n    name = TextField()\n\n    @hybrid_property\n    def quantity(self):\n        return sum([item.qt for item in self.items])\n\n    @quantity.expression\n    def quantity(cls):\n        return fn.SUM(Item.qt).alias('quantity')\n\nclass Item(TestModel):\n    order = ForeignKeyField(Order, backref='items')\n    qt = IntegerField()\n\n\nclass TestHybridWithRelationship(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Order, Item]\n\n    def test_hybrid_with_relationship(self):\n        data = (\n            ('a', (4, 3, 2, 1)),\n            ('b', (1000, 300, 30, 7)),\n            ('c', ()))\n        for name, qts in data:\n            o = Order.create(name=name)\n            for qt in qts:\n                Item.create(order=o, qt=qt)\n\n        query = Order.select().order_by(Order.name)\n        self.assertEqual([o.quantity for o in query], [10, 1337, 0])\n\n        query = (Order\n                 .select(Order.name, Order.quantity.alias('sql_qt'))\n                 .join(Item, JOIN.LEFT_OUTER)\n                 .group_by(Order.name)\n                 .order_by(Order.name))\n        self.assertEqual([o.sql_qt for o in query], [10, 1337, None])\n"
  },
  {
    "path": "tests/keys.py",
    "content": "from peewee import *\n\nfrom .base import IS_MYSQL\nfrom .base import IS_SQLITE\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db\nfrom .base import get_in_memory_db\nfrom .base import requires_sqlite\n\n\nclass Package(TestModel):\n    barcode = CharField(unique=True)\n\n\nclass PackageItem(TestModel):\n    title = CharField()\n    package = ForeignKeyField(Package, Package.barcode, backref='items')\n\n\nclass Manufacturer(TestModel):\n    name = CharField()\n\n\nclass Component(TestModel):\n    name = CharField()\n    manufacturer = ForeignKeyField(Manufacturer, null=True)\n\n\nclass Computer(TestModel):\n    hard_drive = ForeignKeyField(Component, backref='c1')\n    memory = ForeignKeyField(Component, backref='c2')\n    processor = ForeignKeyField(Component, backref='c3')\n\n\nclass User(TestModel):\n    username = CharField()\n\n    class Meta:\n        table_name = 'users'\n\n\nclass Relationship(TestModel):\n    from_user = ForeignKeyField(User, backref='relationships')\n    to_user = ForeignKeyField(User, backref='related_to')\n\n\nclass Note(TestModel):\n    user = ForeignKeyField(User, backref='notes')\n    content = TextField()\n\n\nclass CompositeKeyModel(TestModel):\n    f1 = CharField()\n    f2 = IntegerField()\n    f3 = FloatField()\n\n    class Meta:\n        primary_key = CompositeKey('f1', 'f2')\n\n\nclass UserThing(TestModel):\n    thing = CharField()\n    user = ForeignKeyField(User, backref='things')\n\n    class Meta:\n        primary_key = CompositeKey('thing', 'user')\n\n\nclass Post(TestModel):\n    title = CharField()\n\n\nclass Tag(TestModel):\n    tag = CharField()\n\n\nclass TagPostThrough(TestModel):\n    tag = ForeignKeyField(Tag, backref='posts')\n    post = ForeignKeyField(Post, backref='tags')\n\n    class Meta:\n        primary_key = CompositeKey('tag', 'post')\n\n\nclass TagPostThroughAlt(TestModel):\n    tag = ForeignKeyField(Tag, backref='posts_alt')\n    post = ForeignKeyField(Post, backref='tags_alt')\n\n\nclass TestForeignKeyToNonPrimaryKey(ModelTestCase):\n    requires = [Package, PackageItem]\n\n    def setUp(self):\n        super(TestForeignKeyToNonPrimaryKey, self).setUp()\n\n        for barcode in ['101', '102']:\n            Package.create(barcode=barcode)\n            for i in range(2):\n                PackageItem.create(\n                    package=barcode,\n                    title='%s-%s' % (barcode, i))\n\n    def test_fk_resolution(self):\n        pi = PackageItem.get(PackageItem.title == '101-0')\n        self.assertEqual(pi.__data__['package'], '101')\n        self.assertEqual(pi.package, Package.get(Package.barcode == '101'))\n\n    def test_select_generation(self):\n        p = Package.get(Package.barcode == '101')\n        self.assertEqual(\n            [item.title for item in p.items.order_by(PackageItem.title)],\n            ['101-0', '101-1'])\n\n\nclass TestMultipleForeignKey(ModelTestCase):\n    requires = [Manufacturer, Component, Computer]\n    test_values = [\n        ['3TB', '16GB', 'i7'],\n        ['128GB', '1GB', 'ARM'],\n    ]\n\n    def setUp(self):\n        super(TestMultipleForeignKey, self).setUp()\n        intel = Manufacturer.create(name='Intel')\n        amd = Manufacturer.create(name='AMD')\n        kingston = Manufacturer.create(name='Kingston')\n        for hard_drive, memory, processor in self.test_values:\n            c = Computer.create(\n                hard_drive=Component.create(name=hard_drive),\n                memory=Component.create(name=memory, manufacturer=kingston),\n                processor=Component.create(name=processor, manufacturer=intel))\n\n        # The 2nd computer has an AMD processor.\n        c.processor.manufacturer = amd\n        c.processor.save()\n\n    def test_multi_join(self):\n        HDD = Component.alias('hdd')\n        HDDMf = Manufacturer.alias('hddm')\n        Memory = Component.alias('mem')\n        MemoryMf = Manufacturer.alias('memm')\n        Processor = Component.alias('proc')\n        ProcessorMf = Manufacturer.alias('procm')\n        query = (Computer\n                 .select(\n                     Computer,\n                     HDD,\n                     Memory,\n                     Processor,\n                     HDDMf,\n                     MemoryMf,\n                     ProcessorMf)\n                 .join(HDD, on=(\n                     Computer.hard_drive_id == HDD.id).alias('hard_drive'))\n                 .join(\n                     HDDMf,\n                     JOIN.LEFT_OUTER,\n                     on=(HDD.manufacturer_id == HDDMf.id))\n                 .switch(Computer)\n                 .join(Memory, on=(\n                     Computer.memory_id == Memory.id).alias('memory'))\n                 .join(\n                     MemoryMf,\n                     JOIN.LEFT_OUTER,\n                     on=(Memory.manufacturer_id == MemoryMf.id))\n                 .switch(Computer)\n                 .join(Processor, on=(\n                     Computer.processor_id == Processor.id).alias('processor'))\n                 .join(\n                     ProcessorMf,\n                     JOIN.LEFT_OUTER,\n                     on=(Processor.manufacturer_id == ProcessorMf.id))\n                 .order_by(Computer.id))\n\n        with self.assertQueryCount(1):\n            vals = []\n            manufacturers = []\n            for computer in query:\n                components = [\n                    computer.hard_drive,\n                    computer.memory,\n                    computer.processor]\n                vals.append([component.name for component in components])\n                for component in components:\n                    if component.manufacturer:\n                        manufacturers.append(component.manufacturer.name)\n                    else:\n                        manufacturers.append(None)\n\n            self.assertEqual(vals, self.test_values)\n            self.assertEqual(manufacturers, [\n                None, 'Kingston', 'Intel',\n                None, 'Kingston', 'AMD',\n            ])\n\n\nclass TestMultipleForeignKeysJoining(ModelTestCase):\n    requires = [User, Relationship]\n\n    def test_multiple_fks(self):\n        a = User.create(username='a')\n        b = User.create(username='b')\n        c = User.create(username='c')\n\n        self.assertEqual(list(a.relationships), [])\n        self.assertEqual(list(a.related_to), [])\n\n        r_ab = Relationship.create(from_user=a, to_user=b)\n        self.assertEqual(list(a.relationships), [r_ab])\n        self.assertEqual(list(a.related_to), [])\n        self.assertEqual(list(b.relationships), [])\n        self.assertEqual(list(b.related_to), [r_ab])\n\n        r_bc = Relationship.create(from_user=b, to_user=c)\n\n        following = User.select().join(\n            Relationship, on=Relationship.to_user\n        ).where(Relationship.from_user == a)\n        self.assertEqual(list(following), [b])\n\n        followers = User.select().join(\n            Relationship, on=Relationship.from_user\n        ).where(Relationship.to_user == a.id)\n        self.assertEqual(list(followers), [])\n\n        following = User.select().join(\n            Relationship, on=Relationship.to_user\n        ).where(Relationship.from_user == b.id)\n        self.assertEqual(list(following), [c])\n\n        followers = User.select().join(\n            Relationship, on=Relationship.from_user\n        ).where(Relationship.to_user == b.id)\n        self.assertEqual(list(followers), [a])\n\n        following = User.select().join(\n            Relationship, on=Relationship.to_user\n        ).where(Relationship.from_user == c.id)\n        self.assertEqual(list(following), [])\n\n        followers = User.select().join(\n            Relationship, on=Relationship.from_user\n        ).where(Relationship.to_user == c.id)\n        self.assertEqual(list(followers), [b])\n\n\nclass TestCompositePrimaryKey(ModelTestCase):\n    requires = [Tag, Post, TagPostThrough, CompositeKeyModel, User, UserThing]\n\n    def setUp(self):\n        super(TestCompositePrimaryKey, self).setUp()\n        tags = [Tag.create(tag='t%d' % i) for i in range(1, 4)]\n        posts = [Post.create(title='p%d' % i) for i in range(1, 4)]\n        p12 = Post.create(title='p12')\n        for t, p in zip(tags, posts):\n            TagPostThrough.create(tag=t, post=p)\n        TagPostThrough.create(tag=tags[0], post=p12)\n        TagPostThrough.create(tag=tags[1], post=p12)\n\n    def test_create_table_query(self):\n        query, params = TagPostThrough._schema._create_table().query()\n        sql = ('CREATE TABLE IF NOT EXISTS \"tag_post_through\" ('\n               '\"tag_id\" INTEGER NOT NULL, '\n               '\"post_id\" INTEGER NOT NULL, '\n               'PRIMARY KEY (\"tag_id\", \"post_id\"), '\n               'FOREIGN KEY (\"tag_id\") REFERENCES \"tag\" (\"id\"), '\n               'FOREIGN KEY (\"post_id\") REFERENCES \"post\" (\"id\"))')\n        if IS_MYSQL:\n            sql = sql.replace('\"', '`')\n        self.assertEqual(query, sql)\n\n    def test_get_set_id(self):\n        tpt = (TagPostThrough\n               .select()\n               .join(Tag)\n               .switch(TagPostThrough)\n               .join(Post)\n               .order_by(Tag.tag, Post.title)).get()\n        # Sanity check.\n        self.assertEqual(tpt.tag.tag, 't1')\n        self.assertEqual(tpt.post.title, 'p1')\n\n        tag = Tag.select().where(Tag.tag == 't1').get()\n        post = Post.select().where(Post.title == 'p1').get()\n        self.assertEqual(tpt._pk, (tag.id, post.id))\n\n        # set_id is a no-op.\n        with self.assertRaisesCtx(TypeError):\n            tpt._pk = None\n\n        self.assertEqual(tpt._pk, (tag.id, post.id))\n        t3 = Tag.get(Tag.tag == 't3')\n        p3 = Post.get(Post.title == 'p3')\n        tpt._pk = (t3, p3)\n        self.assertEqual(tpt.tag.tag, 't3')\n        self.assertEqual(tpt.post.title, 'p3')\n\n    def test_querying(self):\n        posts = (Post.select()\n                 .join(TagPostThrough)\n                 .join(Tag)\n                 .where(Tag.tag == 't1')\n                 .order_by(Post.title))\n        self.assertEqual([p.title for p in posts], ['p1', 'p12'])\n\n        tags = (Tag.select()\n                .join(TagPostThrough)\n                .join(Post)\n                .where(Post.title == 'p12')\n                .order_by(Tag.tag))\n        self.assertEqual([t.tag for t in tags], ['t1', 't2'])\n\n    def test_composite_key_model(self):\n        CKM = CompositeKeyModel\n        values = [\n            ('a', 1, 1.0),\n            ('a', 2, 2.0),\n            ('b', 1, 1.0),\n            ('b', 2, 2.0)]\n        c1, c2, c3, c4 = [\n            CKM.create(f1=f1, f2=f2, f3=f3) for f1, f2, f3 in values]\n\n        # Update a single row, giving it a new value for `f3`.\n        CKM.update(f3=3.0).where((CKM.f1 == 'a') & (CKM.f2 == 2)).execute()\n\n        c = CKM.get((CKM.f1 == 'a') & (CKM.f2 == 2))\n        self.assertEqual(c.f3, 3.0)\n\n        # Update the `f3` value and call `save()`, triggering an update.\n        c3.f3 = 4.0\n        c3.save()\n\n        c = CKM.get((CKM.f1 == 'b') & (CKM.f2 == 1))\n        self.assertEqual(c.f3, 4.0)\n\n        # Only 1 row updated.\n        query = CKM.select().where(CKM.f3 == 4.0)\n        self.assertEqual(query.count(), 1)\n\n        # Unfortunately this does not work since the original value of the\n        # PK is lost (and hence cannot be used to update).\n        c4.f1 = 'c'\n        c4.save()\n        self.assertRaises(\n            CKM.DoesNotExist,\n            lambda: CKM.get((CKM.f1 == 'c') & (CKM.f2 == 2)))\n\n    def test_count_composite_key(self):\n        CKM = CompositeKeyModel\n        values = [\n            ('a', 1, 1.0),\n            ('a', 2, 2.0),\n            ('b', 1, 1.0),\n            ('b', 2, 1.0)]\n        for f1, f2, f3 in values:\n            CKM.create(f1=f1, f2=f2, f3=f3)\n\n        self.assertEqual(CKM.select().count(), 4)\n        self.assertTrue(CKM.select().where(\n            (CKM.f1 == 'a') &\n            (CKM.f2 == 1)).exists())\n        self.assertFalse(CKM.select().where(\n            (CKM.f1 == 'a') &\n            (CKM.f2 == 3)).exists())\n\n    def test_delete_instance(self):\n        u1, u2 = [User.create(username='u%s' % i) for i in range(2)]\n        ut1 = UserThing.create(thing='t1', user=u1)\n        ut2 = UserThing.create(thing='t2', user=u1)\n        ut3 = UserThing.create(thing='t1', user=u2)\n        ut4 = UserThing.create(thing='t3', user=u2)\n\n        res = ut1.delete_instance()\n        self.assertEqual(res, 1)\n        self.assertEqual(\n            [x.thing for x in UserThing.select().order_by(UserThing.thing)],\n            ['t1', 't2', 't3'])\n\n    def test_composite_key_inheritance(self):\n        class Person(TestModel):\n            first = TextField()\n            last = TextField()\n\n            class Meta:\n                primary_key = CompositeKey('first', 'last')\n\n        self.assertTrue(isinstance(Person._meta.primary_key, CompositeKey))\n        self.assertEqual(Person._meta.primary_key.field_names,\n                         ('first', 'last'))\n\n        class Employee(Person):\n            title = TextField()\n\n        self.assertTrue(isinstance(Employee._meta.primary_key, CompositeKey))\n        self.assertEqual(Employee._meta.primary_key.field_names,\n                         ('first', 'last'))\n        sql = ('CREATE TABLE IF NOT EXISTS \"employee\" ('\n               '\"first\" TEXT NOT NULL, \"last\" TEXT NOT NULL, '\n               '\"title\" TEXT NOT NULL, PRIMARY KEY (\"first\", \"last\"))')\n        if IS_MYSQL:\n            sql = sql.replace('\"', '`')\n        self.assertEqual(Employee._schema._create_table().query(), (sql, []))\n\n\nclass TestForeignKeyConstraints(ModelTestCase):\n    requires = [User, Note]\n\n    def setUp(self):\n        super(TestForeignKeyConstraints, self).setUp()\n        self.set_foreign_key_pragma(True)\n\n    def tearDown(self):\n        self.set_foreign_key_pragma(False)\n        super(TestForeignKeyConstraints, self).tearDown()\n\n    def set_foreign_key_pragma(self, is_enabled):\n        if IS_SQLITE:\n            self.database.foreign_keys = 'on' if is_enabled else 'off'\n\n    def test_constraint_exists(self):\n        max_id = User.select(fn.MAX(User.id)).scalar() or 0\n        with self.assertRaisesCtx(IntegrityError):\n            with self.database.atomic():\n                Note.create(user=max_id + 1, content='test')\n\n    @requires_sqlite\n    def test_disable_constraint(self):\n        self.set_foreign_key_pragma(False)\n        Note.create(user=0, content='test')\n\n\nclass FK_A(TestModel):\n    key = CharField(max_length=16, unique=True)\n\nclass FK_B(TestModel):\n    fk_a = ForeignKeyField(FK_A, field='key')\n\n\nclass TestFKtoNonPKField(ModelTestCase):\n    requires = [FK_A, FK_B]\n\n    def test_fk_to_non_pk_field(self):\n        a1 = FK_A.create(key='a1')\n        a2 = FK_A.create(key='a2')\n        b1 = FK_B.create(fk_a=a1)\n        b2 = FK_B.create(fk_a=a2)\n\n        args = (b1.fk_a, b1.fk_a_id, a1, a1.key)\n        for arg in args:\n            query = FK_B.select().where(FK_B.fk_a == arg)\n            self.assertSQL(query, (\n                'SELECT \"t1\".\"id\", \"t1\".\"fk_a_id\" FROM \"fk_b\" AS \"t1\" '\n                'WHERE (\"t1\".\"fk_a_id\" = ?)'), ['a1'])\n            b1_db = query.get()\n            self.assertEqual(b1_db.id, b1.id)\n\n    def test_fk_to_non_pk_insert_update(self):\n        a1 = FK_A.create(key='a1')\n        b1 = FK_B.create(fk_a=a1)\n        self.assertEqual(FK_B.select().where(FK_B.fk_a == a1).count(), 1)\n\n        exprs = (\n            {FK_B.fk_a: a1},\n            {'fk_a': a1},\n            {FK_B.fk_a: a1.key},\n            {'fk_a': a1.key})\n        for n, expr in enumerate(exprs, 2):\n            self.assertTrue(FK_B.insert(expr).execute())\n            self.assertEqual(FK_B.select().where(FK_B.fk_a == a1).count(), n)\n\n        a2 = FK_A.create(key='a2')\n        exprs = (\n            {FK_B.fk_a: a2},\n            {'fk_a': a2},\n            {FK_B.fk_a: a2.key},\n            {'fk_a': a2.key})\n\n        b_list = list(FK_B.select().where(FK_B.fk_a == a1))\n        for i, (b, expr) in enumerate(zip(b_list[1:], exprs), 1):\n            self.assertTrue(FK_B.update(expr).where(FK_B.id == b.id).execute())\n            self.assertEqual(FK_B.select().where(FK_B.fk_a == a2).count(), i)\n\n\nclass TestDeferredForeignKeyIntegration(ModelTestCase):\n    database = get_in_memory_db()\n\n    def test_deferred_fk_simple(self):\n        class Base(TestModel):\n            class Meta:\n                database = self.database\n        class DFFk(Base):\n            fk = DeferredForeignKey('DFPk')\n\n        # Deferred key not bound yet.\n        self.assertTrue(isinstance(DFFk.fk, DeferredForeignKey))\n\n        class DFPk(Base): pass\n\n        # Deferred key is bound correctly.\n        self.assertTrue(isinstance(DFFk.fk, ForeignKeyField))\n        self.assertEqual(DFFk.fk.rel_model, DFPk)\n        self.assertEqual(DFFk._meta.refs, {DFFk.fk: DFPk})\n        self.assertEqual(DFFk._meta.backrefs, {})\n        self.assertEqual(DFPk._meta.refs, {})\n        self.assertEqual(DFPk._meta.backrefs, {DFFk.fk: DFFk})\n        self.assertSQL(DFFk._schema._create_table(False), (\n            'CREATE TABLE \"df_fk\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"fk_id\" INTEGER NOT NULL)'), [])\n\n    def test_deferred_fk_as_pk(self):\n        class Base(TestModel):\n            class Meta:\n                database = self.database\n        class DFFk(Base):\n            fk = DeferredForeignKey('DFPk', primary_key=True)\n\n        # Deferred key not bound yet.\n        self.assertTrue(isinstance(DFFk.fk, DeferredForeignKey))\n        self.assertTrue(DFFk._meta.primary_key is DFFk.fk)\n\n        class DFPk(Base): pass\n\n        # Resolved and primary-key set correctly.\n        self.assertTrue(isinstance(DFFk.fk, ForeignKeyField))\n        self.assertTrue(DFFk._meta.primary_key is DFFk.fk)\n\n        self.assertEqual(DFFk.fk.rel_model, DFPk)\n        self.assertEqual(DFFk._meta.refs, {DFFk.fk: DFPk})\n        self.assertEqual(DFFk._meta.backrefs, {})\n        self.assertEqual(DFPk._meta.refs, {})\n        self.assertEqual(DFPk._meta.backrefs, {DFFk.fk: DFFk})\n        self.assertSQL(DFFk._schema._create_table(False), (\n            'CREATE TABLE \"df_fk\" (\"fk_id\" INTEGER NOT NULL PRIMARY KEY)'), [])\n"
  },
  {
    "path": "tests/kv.py",
    "content": "from peewee import IntegerField\nfrom playhouse.kv import KeyValue\n\nfrom .base import DatabaseTestCase\nfrom .base import db\n\n\nclass TestKeyValue(DatabaseTestCase):\n    def setUp(self):\n        super(TestKeyValue, self).setUp()\n        self._kvs = []\n\n    def tearDown(self):\n        if self._kvs:\n            self.database.drop_tables([kv.model for kv in self._kvs])\n        super(TestKeyValue, self).tearDown()\n\n    def create_kv(self, **kwargs):\n        kv = KeyValue(database=self.database, **kwargs)\n        self._kvs.append(kv)\n        return kv\n\n    def test_basic_apis(self):\n        KV = self.create_kv()\n        KV['k1'] = 'v1'\n        KV['k2'] = [0, 1, 2]\n\n        self.assertEqual(KV['k1'], 'v1')\n        self.assertEqual(KV['k2'], [0, 1, 2])\n        self.assertRaises(KeyError, lambda: KV['k3'])\n\n        self.assertTrue((KV.key < 'k2') in KV)\n        self.assertFalse((KV.key > 'k2') in KV)\n\n        del KV['k1']\n        KV['k3'] = 'v3'\n\n        self.assertFalse('k1' in KV)\n        self.assertTrue('k3' in KV)\n        self.assertEqual(sorted(KV.keys()), ['k2', 'k3'])\n        self.assertEqual(len(KV), 2)\n\n        data = dict(KV)\n        self.assertEqual(data, {\n            'k2': [0, 1, 2],\n            'k3': 'v3'})\n\n        self.assertEqual(dict(KV), dict(KV.items()))\n\n        self.assertEqual(KV.pop('k2'), [0, 1, 2])\n        self.assertRaises(KeyError, lambda: KV['k2'])\n        self.assertRaises(KeyError, KV.pop, 'k2')\n\n        self.assertEqual(KV.get('k3'), 'v3')\n        self.assertTrue(KV.get('kx') is None)\n        self.assertEqual(KV.get('kx', 'vx'), 'vx')\n\n        self.assertTrue(KV.get('k4') is None)\n        self.assertEqual(KV.setdefault('k4', 'v4'), 'v4')\n        self.assertEqual(KV.get('k4'), 'v4')\n        self.assertEqual(KV.get('k4', 'v5'),  'v4')\n\n        KV.clear()\n        self.assertEqual(len(KV), 0)\n\n    def test_update(self):\n        KV = self.create_kv()\n        with self.assertQueryCount(1):\n            KV.update(k1='v1', k2='v2', k3='v3')\n\n        self.assertEqual(len(KV), 3)\n\n        with self.assertQueryCount(1):\n            KV.update(k1='v1-x', k3='v3-x', k4='v4')\n        self.assertEqual(len(KV), 4)\n\n        self.assertEqual(dict(KV), {\n            'k1': 'v1-x',\n            'k2': 'v2',\n            'k3': 'v3-x',\n            'k4': 'v4'})\n\n        KV['k1'] = 'v1-y'\n        self.assertEqual(len(KV), 4)\n\n        self.assertEqual(dict(KV), {\n            'k1': 'v1-y',\n            'k2': 'v2',\n            'k3': 'v3-x',\n            'k4': 'v4'})\n\n    def test_expressions(self):\n        KV = self.create_kv(value_field=IntegerField(), ordered=True)\n        with self.database.atomic():\n            for i in range(1, 11):\n                KV['k%d' % i] = i\n\n        self.assertEqual(KV[KV.key < 'k2'], [1, 10])\n        self.assertEqual(KV[KV.value > 7], [10, 8, 9])\n        self.assertEqual(KV[(KV.key > 'k2') & (KV.key < 'k6')], [3, 4, 5])\n        self.assertEqual(KV[KV.key == 'kx'], [])\n\n        del KV[KV.key > 'k3']\n        self.assertEqual(dict(KV), {\n            'k1': 1,\n            'k2': 2,\n            'k3': 3,\n            'k10': 10})\n\n        KV[KV.value > 2] = 99\n        self.assertEqual(dict(KV), {\n            'k1': 1,\n            'k2': 2,\n            'k3': 99,\n            'k10': 99})\n\n    def test_integer_keys(self):\n        KV = self.create_kv(key_field=IntegerField(primary_key=True),\n                            ordered=True)\n        KV[1] = 'v1'\n        KV[2] = 'v2'\n        KV[10] = 'v10'\n        self.assertEqual(list(KV), [(1, 'v1'), (2, 'v2'), (10, 'v10')])\n        self.assertEqual(list(KV.keys()), [1, 2, 10])\n        self.assertEqual(list(KV.values()), ['v1', 'v2', 'v10'])\n\n        del KV[2]\n        KV[1] = 'v1-x'\n        KV[3] = 'v3'\n        self.assertEqual(dict(KV), {\n            1: 'v1-x',\n            3: 'v3',\n            10: 'v10'})\n"
  },
  {
    "path": "tests/manytomany.py",
    "content": "from peewee import *\n\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import get_in_memory_db\nfrom .base import requires_models\nfrom .base_models import Tweet\nfrom .base_models import User\n\n\nclass User(TestModel):\n    username = TextField(unique=True)\n\nclass Note(TestModel):\n    text = TextField()\n    users = ManyToManyField(User)\n\nNoteUserThrough = Note.users.get_through_model()\n\nAltThroughDeferred = DeferredThroughModel()\n\nclass AltNote(TestModel):\n    text = TextField()\n    users = ManyToManyField(User, through_model=AltThroughDeferred)\n\nclass AltThroughModel(TestModel):\n    user = ForeignKeyField(User, backref='_xx_rel')\n    note = ForeignKeyField(AltNote, backref='_xx_rel')\n\n    class Meta:\n        primary_key = CompositeKey('user', 'note')\n\nAltThroughDeferred.set_model(AltThroughModel)\n\nclass Student(TestModel):\n    name = TextField()\n\nCourseStudentDeferred = DeferredThroughModel()\n\nclass Course(TestModel):\n    name = TextField()\n    students = ManyToManyField(Student, backref='+')\n    students2 = ManyToManyField(Student, through_model=CourseStudentDeferred)\n\nCourseStudent = Course.students.get_through_model()\n\nclass CourseStudent2(TestModel):\n    course = ForeignKeyField(Course, backref='+')\n    student = ForeignKeyField(Student, backref='+')\n\nCourseStudentDeferred.set_model(CourseStudent2)\n\n\nclass Color(TestModel):\n    name = TextField(unique=True)\n\nLogoColorDeferred = DeferredThroughModel()\n\nclass Logo(TestModel):\n    name = TextField(unique=True)\n    colors = ManyToManyField(Color, through_model=LogoColorDeferred)\n\nclass LogoColor(TestModel):\n    logo = ForeignKeyField(Logo, field=Logo.name)\n    color = ForeignKeyField(Color, field=Color.name)  # FK to non-PK column.\n\nLogoColorDeferred.set_model(LogoColor)\n\n\nclass TestManyToManyFKtoNonPK(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Color, Logo, LogoColor]\n\n    def test_manytomany_fk_to_non_pk(self):\n        red = Color.create(name='red')\n        green = Color.create(name='green')\n        blue = Color.create(name='blue')\n        lrg = Logo.create(name='logo-rg')\n        lrb = Logo.create(name='logo-rb')\n        lrgb = Logo.create(name='logo-rgb')\n        lrg.colors.add([red, green])\n        lrb.colors.add([red, blue])\n        lrgb.colors.add([red, green, blue])\n\n        def assertColors(logo, expected):\n            colors = [c.name for c in logo.colors.order_by(Color.name)]\n            self.assertEqual(colors, expected)\n\n        assertColors(lrg, ['green', 'red'])\n        assertColors(lrb, ['blue', 'red'])\n        assertColors(lrgb, ['blue', 'green', 'red'])\n\n        def assertLogos(color, expected):\n            logos = [l.name for l in color.logos.order_by(Logo.name)]\n            self.assertEqual(logos, expected)\n\n        assertLogos(red, ['logo-rb', 'logo-rg', 'logo-rgb'])\n        assertLogos(green, ['logo-rg', 'logo-rgb'])\n        assertLogos(blue, ['logo-rb', 'logo-rgb'])\n\n        # Verify we can delete data as well.\n        lrg.colors.remove(red)\n        self.assertEqual([c.name for c in lrg.colors], ['green'])\n\n        blue.logos.remove(lrb)\n        self.assertEqual([c.name for c in lrb.colors], ['red'])\n\n        # Verify we can insert using a SELECT query.\n        lrg.colors.add(Color.select().where(Color.name != 'blue'), True)\n        assertColors(lrg, ['green', 'red'])\n\n        lrb.colors.add(Color.select().where(Color.name == 'blue'))\n        assertColors(lrb, ['blue', 'red'])\n\n        # Verify we can insert logos using a SELECT query.\n        black = Color.create(name='black')\n        black.logos.add(Logo.select().where(Logo.name != 'logo-rgb'))\n        assertLogos(black, ['logo-rb', 'logo-rg'])\n        assertColors(lrb, ['black', 'blue', 'red'])\n        assertColors(lrg, ['black', 'green', 'red'])\n        assertColors(lrgb, ['blue', 'green', 'red'])\n\n        # Verify we can delete using a SELECT query.\n        lrg.colors.remove(Color.select().where(Color.name == 'red'))\n        assertColors(lrg, ['black', 'green'])\n\n        black.logos.remove(Logo.select().where(Logo.name == 'logo-rg'))\n        assertLogos(black, ['logo-rb'])\n\n        # Verify we can clear.\n        lrg.colors.clear()\n        assertColors(lrg, [])\n        assertColors(lrb, ['black', 'blue', 'red'])  # Not affected.\n\n        black.logos.clear()\n        assertLogos(black, [])\n        assertLogos(red, ['logo-rb', 'logo-rgb'])\n\n\nclass TestManyToManyBackrefBehavior(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Student, Course, CourseStudent, CourseStudent2]\n\n    def setUp(self):\n        super(TestManyToManyBackrefBehavior, self).setUp()\n        math = Course.create(name='math')\n        engl = Course.create(name='engl')\n        huey, mickey, zaizee = [Student.create(name=name)\n                                for name in ('huey', 'mickey', 'zaizee')]\n        # Set up relationships.\n        math.students.add([huey, zaizee])\n        engl.students.add([mickey])\n        math.students2.add([mickey])\n        engl.students2.add([huey, zaizee])\n\n    def test_manytomanyfield_disabled_backref(self):\n        math = Course.get(name='math')\n        query = math.students.order_by(Student.name)\n        self.assertEqual([s.name for s in query], ['huey', 'zaizee'])\n\n        huey = Student.get(name='huey')\n        math.students.remove(huey)\n        self.assertEqual([s.name for s in math.students], ['zaizee'])\n\n        # The backref is via the CourseStudent2 through-model.\n        self.assertEqual([c.name for c in huey.courses], ['engl'])\n\n    def test_through_model_disabled_backrefs(self):\n        # Here we're testing the case where the many-to-many field does not\n        # explicitly disable back-references, but the foreign-keys on the\n        # through model have disabled back-references.\n        engl = Course.get(name='engl')\n        query = engl.students2.order_by(Student.name)\n        self.assertEqual([s.name for s in query], ['huey', 'zaizee'])\n\n        zaizee = Student.get(Student.name == 'zaizee')\n        engl.students2.remove(zaizee)\n        self.assertEqual([s.name for s in engl.students2], ['huey'])\n\n        math = Course.get(name='math')\n        self.assertEqual([s.name for s in math.students2], ['mickey'])\n\n\nclass TestManyToManyInheritance(ModelTestCase):\n    def test_manytomany_inheritance(self):\n        class BaseModel(TestModel):\n            class Meta:\n                database = self.database\n        class User(BaseModel):\n            username = TextField()\n        class Project(BaseModel):\n            name = TextField()\n            users = ManyToManyField(User, backref='projects')\n\n        def subclass_project():\n            class VProject(Project):\n                pass\n\n        # We cannot subclass Project, because the many-to-many field \"users\"\n        # will be inherited, but the through-model does not contain a\n        # foreign-key to VProject. The through-model in this case is\n        # ProjectUsers, which has foreign-keys to project and user.\n        self.assertRaises(ValueError, subclass_project)\n        PThrough = Project.users.through_model\n        self.assertTrue(PThrough.project.rel_model is Project)\n        self.assertTrue(PThrough.user.rel_model is User)\n\n\nclass TestManyToMany(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User, Note, NoteUserThrough, AltNote, AltThroughModel]\n\n    user_to_note = {\n        'gargie': [1, 2],\n        'huey': [2, 3],\n        'mickey': [3, 4],\n        'zaizee': [4, 5],\n    }\n\n    def setUp(self):\n        super(TestManyToMany, self).setUp()\n        for username in sorted(self.user_to_note):\n            User.create(username=username)\n        for i in range(5):\n            Note.create(text='note-%s' % (i + 1))\n\n    def test_through_model(self):\n        self.assertEqual(len(NoteUserThrough._meta.fields), 3)\n        fields = NoteUserThrough._meta.fields\n        self.assertEqual(sorted(fields), ['id', 'note', 'user'])\n\n        note_field = fields['note']\n        self.assertEqual(note_field.rel_model, Note)\n        self.assertFalse(note_field.null)\n\n        user_field = fields['user']\n        self.assertEqual(user_field.rel_model, User)\n        self.assertFalse(user_field.null)\n\n    def _set_data(self):\n        for username, notes in self.user_to_note.items():\n            user = User.get(User.username == username)\n            for note in notes:\n                NoteUserThrough.create(\n                    note=Note.get(Note.text == 'note-%s' % note),\n                    user=user)\n\n    def assertNotes(self, query, expected):\n        notes = [note.text for note in query]\n        self.assertEqual(sorted(notes),\n                         ['note-%s' % i for i in sorted(expected)])\n\n    def assertUsers(self, query, expected):\n        usernames = [user.username for user in query]\n        self.assertEqual(sorted(usernames), sorted(expected))\n\n    def test_accessor_query(self):\n        self._set_data()\n        gargie, huey, mickey, zaizee = User.select().order_by(User.username)\n\n        with self.assertQueryCount(1):\n            self.assertNotes(gargie.notes, [1, 2])\n        with self.assertQueryCount(1):\n            self.assertNotes(zaizee.notes, [4, 5])\n        with self.assertQueryCount(2):\n            self.assertNotes(User.create(username='x').notes, [])\n\n        n1, n2, n3, n4, n5 = Note.select().order_by(Note.text)\n        with self.assertQueryCount(1):\n            self.assertUsers(n1.users, ['gargie'])\n        with self.assertQueryCount(1):\n            self.assertUsers(n2.users, ['gargie', 'huey'])\n        with self.assertQueryCount(1):\n            self.assertUsers(n5.users, ['zaizee'])\n        with self.assertQueryCount(2):\n            self.assertUsers(Note.create(text='x').users, [])\n\n    def test_prefetch_notes(self):\n        self._set_data()\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(3):\n                gargie, huey, mickey, zaizee = prefetch(\n                    User.select().order_by(User.username),\n                    NoteUserThrough,\n                    Note,\n                    prefetch_type=pt)\n\n            with self.assertQueryCount(0):\n                self.assertNotes(gargie.notes, [1, 2])\n            with self.assertQueryCount(0):\n                self.assertNotes(zaizee.notes, [4, 5])\n        with self.assertQueryCount(2):\n            self.assertNotes(User.create(username='x').notes, [])\n\n    def test_prefetch_users(self):\n        self._set_data()\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(3):\n                n1, n2, n3, n4, n5 = prefetch(\n                    Note.select().order_by(Note.text),\n                    NoteUserThrough,\n                    User,\n                    prefetch_type=pt)\n\n            with self.assertQueryCount(0):\n                self.assertUsers(n1.users, ['gargie'])\n            with self.assertQueryCount(0):\n                self.assertUsers(n2.users, ['gargie', 'huey'])\n            with self.assertQueryCount(0):\n                self.assertUsers(n5.users, ['zaizee'])\n        with self.assertQueryCount(2):\n            self.assertUsers(Note.create(text='x').users, [])\n\n    def test_query_filtering(self):\n        self._set_data()\n        gargie, huey, mickey, zaizee = User.select().order_by(User.username)\n\n        with self.assertQueryCount(1):\n            notes = gargie.notes.where(Note.text != 'note-2')\n            self.assertNotes(notes, [1])\n\n    def test_set_value(self):\n        self._set_data()\n        gargie = User.get(User.username == 'gargie')\n        huey = User.get(User.username == 'huey')\n        n1, n2, n3, n4, n5 = Note.select().order_by(Note.text)\n\n        with self.assertQueryCount(2):\n            gargie.notes = n3\n        self.assertNotes(gargie.notes, [3])\n        self.assertUsers(n3.users, ['gargie', 'huey', 'mickey'])\n        self.assertUsers(n1.users, [])\n\n        gargie.notes = [n3, n4]\n        self.assertNotes(gargie.notes, [3, 4])\n        self.assertUsers(n3.users, ['gargie', 'huey', 'mickey'])\n        self.assertUsers(n4.users, ['gargie', 'mickey', 'zaizee'])\n\n    def test_set_query(self):\n        huey = User.get(User.username == 'huey')\n\n        with self.assertQueryCount(2):\n            huey.notes = Note.select().where(~Note.text.endswith('4'))\n        self.assertNotes(huey.notes, [1, 2, 3, 5])\n\n    def test_add(self):\n        gargie = User.get(User.username == 'gargie')\n        huey = User.get(User.username == 'huey')\n        n1, n2, n3, n4, n5 = Note.select().order_by(Note.text)\n\n        gargie.notes.add([n1, n2])\n        self.assertNotes(gargie.notes, [1, 2])\n        self.assertUsers(n1.users, ['gargie'])\n        self.assertUsers(n2.users, ['gargie'])\n        for note in [n3, n4, n5]:\n            self.assertUsers(note.users, [])\n\n        with self.assertQueryCount(1):\n            huey.notes.add(Note.select().where(\n                fn.substr(Note.text, 6, 1) << ['1', '3', '5']))\n\n        self.assertNotes(huey.notes, [1, 3, 5])\n        self.assertUsers(n1.users, ['gargie', 'huey'])\n        self.assertUsers(n2.users, ['gargie'])\n        self.assertUsers(n3.users, ['huey'])\n        self.assertUsers(n4.users, [])\n        self.assertUsers(n5.users, ['huey'])\n\n        with self.assertQueryCount(1):\n            gargie.notes.add(n4)\n        self.assertNotes(gargie.notes, [1, 2, 4])\n\n        with self.assertQueryCount(2):\n            n3.users.add(\n                User.select().where(User.username != 'gargie'),\n                clear_existing=True)\n        self.assertUsers(n3.users, ['huey', 'mickey', 'zaizee'])\n\n    def test_add_by_pk(self):\n        huey = User.get(User.username == 'huey')\n        n1, n2, n3 = Note.select().order_by(Note.text).limit(3)\n        huey.notes.add([n1.id, n2.id])\n        self.assertNotes(huey.notes, [1, 2])\n        self.assertUsers(n1.users, ['huey'])\n        self.assertUsers(n2.users, ['huey'])\n        self.assertUsers(n3.users, [])\n\n    def test_unique(self):\n        n1 = Note.get(Note.text == 'note-1')\n        huey = User.get(User.username == 'huey')\n\n        def add_user(note, user):\n            with self.assertQueryCount(1):\n                note.users.add(user)\n\n        add_user(n1, huey)\n        self.assertRaises(IntegrityError, add_user, n1, huey)\n\n        add_user(n1, User.get(User.username == 'zaizee'))\n        self.assertUsers(n1.users, ['huey', 'zaizee'])\n\n    def test_remove(self):\n        self._set_data()\n        gargie, huey, mickey, zaizee = User.select().order_by(User.username)\n        n1, n2, n3, n4, n5 = Note.select().order_by(Note.text)\n\n        with self.assertQueryCount(1):\n            gargie.notes.remove([n1, n2, n3])\n\n        self.assertNotes(gargie.notes, [])\n        self.assertNotes(huey.notes, [2, 3])\n\n        with self.assertQueryCount(1):\n            huey.notes.remove(Note.select().where(\n                Note.text << ['note-2', 'note-4', 'note-5']))\n\n        self.assertNotes(huey.notes, [3])\n        self.assertNotes(mickey.notes, [3, 4])\n        self.assertNotes(zaizee.notes, [4, 5])\n\n        with self.assertQueryCount(1):\n            n4.users.remove([gargie, mickey])\n        self.assertUsers(n4.users, ['zaizee'])\n\n        with self.assertQueryCount(1):\n            n5.users.remove(User.select())\n        self.assertUsers(n5.users, [])\n\n    def test_remove_by_id(self):\n        self._set_data()\n        gargie, huey = User.select().order_by(User.username).limit(2)\n        n1, n2, n3, n4 = Note.select().order_by(Note.text).limit(4)\n        gargie.notes.add([n3, n4])\n\n        with self.assertQueryCount(1):\n            gargie.notes.remove([n1.id, n3.id])\n\n        self.assertNotes(gargie.notes, [2, 4])\n        self.assertNotes(huey.notes, [2, 3])\n\n    def test_clear(self):\n        gargie = User.get(User.username == 'gargie')\n        huey = User.get(User.username == 'huey')\n\n        gargie.notes = Note.select()\n        huey.notes = Note.select()\n\n        self.assertEqual(gargie.notes.count(), 5)\n        self.assertEqual(huey.notes.count(), 5)\n\n        gargie.notes.clear()\n        self.assertEqual(gargie.notes.count(), 0)\n        self.assertEqual(huey.notes.count(), 5)\n\n        n1 = Note.get(Note.text == 'note-1')\n        n2 = Note.get(Note.text == 'note-2')\n\n        n1.users = User.select()\n        n2.users = User.select()\n\n        self.assertEqual(n1.users.count(), 4)\n        self.assertEqual(n2.users.count(), 4)\n\n        n1.users.clear()\n        self.assertEqual(n1.users.count(), 0)\n        self.assertEqual(n2.users.count(), 4)\n\n    def test_manual_through(self):\n        gargie, huey, mickey, zaizee = User.select().order_by(User.username)\n        alt_notes = []\n        for i in range(5):\n            alt_notes.append(AltNote.create(text='note-%s' % (i + 1)))\n\n        self.assertNotes(gargie.altnotes, [])\n        for alt_note in alt_notes:\n            self.assertUsers(alt_note.users, [])\n\n        n1, n2, n3, n4, n5 = alt_notes\n\n        # Test adding relationships by setting the descriptor.\n        gargie.altnotes = [n1, n2]\n\n        with self.assertQueryCount(2):\n            huey.altnotes = AltNote.select().where(\n                fn.substr(AltNote.text, 6, 1) << ['1', '3', '5'])\n\n        mickey.altnotes.add([n1, n4])\n\n        with self.assertQueryCount(2):\n            zaizee.altnotes = AltNote.select()\n\n        # Test that the notes were added correctly.\n        with self.assertQueryCount(1):\n            self.assertNotes(gargie.altnotes, [1, 2])\n\n        with self.assertQueryCount(1):\n            self.assertNotes(huey.altnotes, [1, 3, 5])\n\n        with self.assertQueryCount(1):\n            self.assertNotes(mickey.altnotes, [1, 4])\n\n        with self.assertQueryCount(1):\n            self.assertNotes(zaizee.altnotes, [1, 2, 3, 4, 5])\n\n        # Test removing notes.\n        with self.assertQueryCount(1):\n            gargie.altnotes.remove(n1)\n        self.assertNotes(gargie.altnotes, [2])\n\n        with self.assertQueryCount(1):\n            huey.altnotes.remove([n1, n2, n3])\n        self.assertNotes(huey.altnotes, [5])\n\n        with self.assertQueryCount(1):\n            sq = (AltNote\n                  .select()\n                  .where(fn.SUBSTR(AltNote.text, 6, 1) << ['1', '2', '4']))\n            zaizee.altnotes.remove(sq)\n        self.assertNotes(zaizee.altnotes, [3, 5])\n\n        # Test the backside of the relationship.\n        n1.users = User.select().where(User.username != 'gargie')\n\n        with self.assertQueryCount(1):\n            self.assertUsers(n1.users, ['huey', 'mickey', 'zaizee'])\n        with self.assertQueryCount(1):\n            self.assertUsers(n2.users, ['gargie'])\n        with self.assertQueryCount(1):\n            self.assertUsers(n3.users, ['zaizee'])\n        with self.assertQueryCount(1):\n            self.assertUsers(n4.users, ['mickey'])\n        with self.assertQueryCount(1):\n            self.assertUsers(n5.users, ['huey', 'zaizee'])\n\n        with self.assertQueryCount(1):\n            n1.users.remove(User.select())\n        with self.assertQueryCount(1):\n            n5.users.remove([gargie, huey])\n\n        with self.assertQueryCount(1):\n            self.assertUsers(n1.users, [])\n        with self.assertQueryCount(1):\n            self.assertUsers(n5.users, ['zaizee'])\n\n\nclass Person(TestModel):\n    name = CharField()\n\nclass Account(TestModel):\n    person = ForeignKeyField(Person, primary_key=True)\n\nclass AccountList(TestModel):\n    name = CharField()\n    accounts = ManyToManyField(Account, backref='lists')\n\nAccountListThrough = AccountList.accounts.get_through_model()\n\n\nclass TestForeignKeyPrimaryKeyManyToMany(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Person, Account, AccountList, AccountListThrough]\n    test_data = (\n        ('huey', ('cats', 'evil')),\n        ('zaizee', ('cats', 'good')),\n        ('mickey', ('dogs', 'good')),\n        ('zombie', ()),\n    )\n\n    def setUp(self):\n        super(TestForeignKeyPrimaryKeyManyToMany, self).setUp()\n\n        name2list = {}\n        for name, lists in self.test_data:\n            p = Person.create(name=name)\n            a = Account.create(person=p)\n            for l in lists:\n                if l not in name2list:\n                    name2list[l] = AccountList.create(name=l)\n                name2list[l].accounts.add(a)\n\n    def account_for(self, name):\n        return Account.select().join(Person).where(Person.name == name).get()\n\n    def assertLists(self, l1, l2):\n        self.assertEqual(sorted(list(l1)), sorted(list(l2)))\n\n    def test_pk_is_fk(self):\n        list2names = {}\n        for name, lists in self.test_data:\n            account = self.account_for(name)\n            self.assertLists([l.name for l in account.lists],\n                             lists)\n            for l in lists:\n                list2names.setdefault(l, [])\n                list2names[l].append(name)\n\n        for list_name, names in list2names.items():\n            account_list = AccountList.get(AccountList.name == list_name)\n            self.assertLists([s.person.name for s in account_list.accounts],\n                             names)\n\n    def test_empty(self):\n        al = AccountList.create(name='empty')\n        self.assertEqual(list(al.accounts), [])\n\n\nclass Permission(TestModel):\n    name = TextField()\n\nDeniedThroughDeferred = DeferredThroughModel()\n\nclass Visitor(TestModel):\n    name = TextField()\n    allowed = ManyToManyField(Permission)\n    denied = ManyToManyField(Permission, through_model=DeniedThroughDeferred)\n\nclass DeniedThrough(TestModel):\n    permission = ForeignKeyField(Permission)\n    visitor = ForeignKeyField(Visitor)\n\nDeniedThroughDeferred.set_model(DeniedThrough)\n\n\nclass TestMultipleManyToManySameTables(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Permission, Visitor, Visitor.allowed.through_model,\n                Visitor.denied.through_model]\n\n    def test_multiple_manytomany_same_tables(self):\n        p1, p2, p3 = [Permission.create(name=n) for n in ('p1', 'p2', 'p3')]\n        v1, v2, v3 = [Visitor.create(name=n) for n in ('v1', 'v2', 'v3')]\n\n        v1.allowed.add([p1, p2, p3])\n        v2.allowed.add(p2)\n        v2.denied.add([p1, p3])\n        v3.allowed.add(p3)\n        v3.denied.add(p1)\n\n        accum = []\n        for v in Visitor.select().order_by(Visitor.name):\n            allowed, denied = [], []\n            for p in v.allowed.order_by(Permission.name):\n                allowed.append(p.name)\n            for p in v.denied.order_by(Permission.name):\n                denied.append(p.name)\n            accum.append((v.name, allowed, denied))\n\n        self.assertEqual(accum, [\n            ('v1', ['p1', 'p2', 'p3'], []),\n            ('v2', ['p2'], ['p1', 'p3']),\n            ('v3', ['p3'], ['p1'])])\n"
  },
  {
    "path": "tests/migrations.py",
    "content": "import datetime\nimport os\nfrom functools import partial\n\nfrom peewee import *\nfrom playhouse.migrate import *\nfrom .base import BaseTestCase\nfrom .base import IS_CRDB\nfrom .base import IS_MYSQL\nfrom .base import IS_POSTGRESQL\nfrom .base import IS_PSYCOPG3\nfrom .base import IS_SQLITE\nfrom .base import IS_SQLITE_25\nfrom .base import IS_SQLITE_35\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db\nfrom .base import get_in_memory_db\nfrom .base import requires_models\nfrom .base import requires_pglike\nfrom .base import requires_postgresql\nfrom .base import requires_sqlite\nfrom .base import skip_if\nfrom .base import skip_unless\n\ntry:\n    from psycopg2cffi import compat\n    compat.register()\nexcept ImportError:\n    pass\n\n\nclass Tag(TestModel):\n    tag = CharField()\n\nclass Person(TestModel):\n    first_name = CharField()\n    last_name = CharField()\n    dob = DateField(null=True)\n\nclass User(TestModel):\n    id = CharField(primary_key=True, max_length=20)\n    password = CharField(default='secret')\n\n    class Meta:\n        table_name = 'users'\n\nclass Page(TestModel):\n    name = CharField(max_length=100, unique=True, null=True)\n    user = ForeignKeyField(User, null=True, backref='pages')\n\nclass Session(TestModel):\n    user = ForeignKeyField(User, unique=True, backref='sessions')\n    updated_at = DateField(null=True)\n\nclass IndexModel(TestModel):\n    first_name = CharField()\n    last_name = CharField()\n    data = IntegerField(unique=True)\n\n    class Meta:\n        indexes = (\n            (('first_name', 'last_name'), True),\n        )\n\nclass Category(TestModel):\n    name = TextField()\n\n\nclass TestSchemaMigration(ModelTestCase):\n    requires = [Person, Tag, User, Page, Session]\n\n    # Each database behaves slightly differently.\n    _exception_add_not_null = not IS_MYSQL\n\n    _person_data = [\n        ('Charlie', 'Leifer', None),\n        ('Huey', 'Kitty', datetime.date(2011, 5, 1)),\n        ('Mickey', 'Dog', datetime.date(2008, 6, 1)),\n    ]\n\n    def setUp(self):\n        super(TestSchemaMigration, self).setUp()\n        self.migrator = SchemaMigrator.from_database(self.database)\n\n    def tearDown(self):\n        try:\n            super(TestSchemaMigration, self).tearDown()\n        finally:\n            self.database.close()\n\n    @requires_pglike\n    def test_add_table_constraint(self):\n        price = FloatField(default=0.)\n        migrate(self.migrator.add_column('tag', 'price', price),\n                self.migrator.add_constraint('tag', 'price_check',\n                                             Check('price >= 0')))\n        class Tag2(Model):\n            tag = CharField()\n            price = FloatField(default=0.)\n            class Meta:\n                database = self.database\n                table_name = Tag._meta.table_name\n\n        with self.database.atomic():\n            self.assertRaises(IntegrityError, Tag2.create, tag='t1', price=-1)\n\n        Tag2.create(tag='t1', price=1.0)\n        t1_db = Tag2.get(Tag2.tag == 't1')\n        self.assertEqual(t1_db.price, 1.0)\n\n    @skip_if(IS_SQLITE)\n    def test_add_unique(self):\n        alt_id = IntegerField(default=0)\n        migrate(\n            self.migrator.add_column('tag', 'alt_id', alt_id),\n            self.migrator.add_unique('tag', 'alt_id'))\n\n        class Tag2(Model):\n            tag = CharField()\n            alt_id = IntegerField(default=0)\n            class Meta:\n                database = self.database\n                table_name = Tag._meta.table_name\n\n        Tag2.create(tag='t1', alt_id=1)\n        with self.database.atomic():\n            self.assertRaises(IntegrityError, Tag2.create, tag='t2', alt_id=1)\n\n    @requires_pglike\n    def test_drop_table_constraint(self):\n        price = FloatField(default=0.)\n        migrate(\n            self.migrator.add_column('tag', 'price', price),\n            self.migrator.add_constraint('tag', 'price_check',\n                                         Check('price >= 0')))\n\n        class Tag2(Model):\n            tag = CharField()\n            price = FloatField(default=0.)\n            class Meta:\n                database = self.database\n                table_name = Tag._meta.table_name\n\n        with self.database.atomic():\n            self.assertRaises(IntegrityError, Tag2.create, tag='t1', price=-1)\n\n        migrate(self.migrator.drop_constraint('tag', 'price_check'))\n        Tag2.create(tag='t1', price=-1)\n        t1_db = Tag2.get(Tag2.tag == 't1')\n        self.assertEqual(t1_db.price, -1.0)\n\n    def test_add_column(self):\n        # Create some fields with a variety of NULL / default values.\n        df = DateTimeField(null=True)\n        df_def = DateTimeField(default=datetime.datetime(2012, 1, 1))\n        cf = CharField(max_length=200, default='')\n        bf = BooleanField(default=True)\n        ff = FloatField(default=0)\n\n        # Create two rows in the Tag table to test the handling of adding\n        # non-null fields.\n        t1 = Tag.create(tag='t1')\n        t2 = Tag.create(tag='t2')\n\n        # Convenience function for generating `add_column` migrations.\n        add_column = partial(self.migrator.add_column, 'tag')\n\n        # Run the migration.\n        migrate(\n            add_column('pub_date', df),\n            add_column('modified_date', df_def),\n            add_column('comment', cf),\n            add_column('is_public', bf),\n            add_column('popularity', ff))\n\n        # Create a new tag model to represent the fields we added.\n        class NewTag(Model):\n            tag = CharField()\n            pub_date = df\n            modified_date = df_def\n            comment = cf\n            is_public = bf\n            popularity = ff\n\n            class Meta:\n                database = self.database\n                table_name = Tag._meta.table_name\n\n        query = (NewTag\n                 .select(\n                     NewTag.id,\n                     NewTag.tag,\n                     NewTag.pub_date,\n                     NewTag.modified_date,\n                     NewTag.comment,\n                     NewTag.is_public,\n                     NewTag.popularity)\n                 .order_by(NewTag.tag.asc()))\n\n        # Verify the resulting rows are correct.\n        self.assertEqual(list(query.tuples()), [\n            (t1.id, 't1', None, datetime.datetime(2012, 1, 1), '', True, 0.0),\n            (t2.id, 't2', None, datetime.datetime(2012, 1, 1), '', True, 0.0),\n        ])\n\n    @skip_if(IS_MYSQL, 'mysql does not support CHECK()')\n    def test_add_column_constraint(self):\n        cf = CharField(null=True, constraints=[SQL('default \\'foo\\'')])\n        ff = FloatField(default=0., constraints=[Check('val < 1.0')])\n        t1 = Tag.create(tag='t1')\n        migrate(\n            self.migrator.add_column('tag', 'misc', cf),\n            self.migrator.add_column('tag', 'val', ff))\n\n        class NewTag(Model):\n            tag = CharField()\n            misc = CharField()\n            val = FloatField()\n            class Meta:\n                database = self.database\n                table_name = Tag._meta.table_name\n\n        t1_db = NewTag.get(NewTag.tag == 't1')\n        self.assertEqual(t1_db.misc, 'foo')\n        self.assertEqual(t1_db.val, 0.)\n\n        with self.database.atomic():\n            self.assertRaises(IntegrityError, NewTag.create, tag='t2',\n                              misc='bar', val=2.)\n\n        NewTag.create(tag='t3', misc='baz', val=0.9)\n        t3_db = NewTag.get(NewTag.tag == 't3')\n        self.assertEqual(t3_db.misc, 'baz')\n        self.assertEqual(t3_db.val, 0.9)\n\n    def _create_people(self):\n        for first, last, dob in self._person_data:\n            Person.create(first_name=first, last_name=last, dob=dob)\n\n    def get_column_names(self, tbl):\n        cursor = self.database.execute_sql('select * from %s limit 1' % tbl)\n        return set([col[0] for col in cursor.description])\n\n    def test_drop_column(self, legacy=False):\n        kw = {'legacy': legacy} if IS_SQLITE else {}\n        self._create_people()\n        migrate(\n            self.migrator.drop_column('person', 'last_name', **kw),\n            self.migrator.drop_column('person', 'dob', **kw))\n\n        column_names = self.get_column_names('person')\n        self.assertEqual(column_names, set(['id', 'first_name']))\n\n        User.create(id='charlie', password='12345')\n        User.create(id='huey', password='meow')\n        migrate(self.migrator.drop_column('users', 'password', **kw))\n\n        column_names = self.get_column_names('users')\n        self.assertEqual(column_names, set(['id']))\n        data = [row for row in User.select(User.id).order_by(User.id).tuples()]\n        self.assertEqual(data, [\n            ('charlie',),\n            ('huey',),])\n\n    @skip_unless(IS_SQLITE_35, 'Requires sqlite 3.35 or newer')\n    def test_drop_column_sqlite_legacy(self):\n        self.test_drop_column(legacy=True)\n\n    def test_rename_column(self, legacy=False):\n        kw = {'legacy': legacy} if IS_SQLITE else {}\n        self._create_people()\n        migrate(\n            self.migrator.rename_column('person', 'first_name', 'first', **kw),\n            self.migrator.rename_column('person', 'last_name', 'last', **kw))\n\n        column_names = self.get_column_names('person')\n        self.assertEqual(column_names, set(['id', 'first', 'last', 'dob']))\n\n        class NewPerson(Model):\n            first = CharField()\n            last = CharField()\n            dob = DateField()\n\n            class Meta:\n                database = self.database\n                table_name = Person._meta.table_name\n\n        query = (NewPerson\n                 .select(\n                     NewPerson.first,\n                     NewPerson.last,\n                     NewPerson.dob)\n                 .order_by(NewPerson.first))\n        self.assertEqual(list(query.tuples()), self._person_data)\n\n    @skip_unless(IS_SQLITE_25, 'Requires sqlite 3.25 or newer')\n    def test_rename_column_sqlite_legacy(self):\n        self.test_rename_column(legacy=True)\n\n    def test_rename_gh380(self, legacy=False):\n        kw = {'legacy': legacy} if IS_SQLITE else {}\n        u1 = User.create(id='charlie')\n        u2 = User.create(id='huey')\n        p1 = Page.create(name='p1-1', user=u1)\n        p2 = Page.create(name='p2-1', user=u1)\n        p3 = Page.create(name='p3-2', user=u2)\n\n        migrate(self.migrator.rename_column('page', 'name', 'title', **kw))\n\n        column_names = self.get_column_names('page')\n        self.assertEqual(column_names, set(['id', 'title', 'user_id']))\n\n        class NewPage(Model):\n            title = CharField(max_length=100, unique=True, null=True)\n            user = ForeignKeyField(User, null=True, backref='newpages')\n\n            class Meta:\n                database = self.database\n                table_name = Page._meta.table_name\n\n        query = (NewPage\n                 .select(\n                     NewPage.title,\n                     NewPage.user)\n                 .order_by(NewPage.title))\n        self.assertEqual(\n            [(np.title, np.user.id) for np in query],\n            [('p1-1', 'charlie'), ('p2-1', 'charlie'), ('p3-2', 'huey')])\n\n    @skip_unless(IS_SQLITE_25, 'Requires sqlite 3.25 or newer')\n    def test_rename_gh380_sqlite_legacy(self):\n        self.test_rename_gh380(legacy=True)\n\n    @skip_if(IS_PSYCOPG3, 'Psycopg3 chokes on the default value.')\n    def test_add_default_drop_default(self):\n        with self.database.transaction():\n            migrate(self.migrator.add_column_default('person', 'first_name',\n                                                     default='x'))\n\n        p = Person.create(last_name='Last')\n        p_db = Person.get(Person.last_name == 'Last')\n        self.assertEqual(p_db.first_name, 'x')\n\n        with self.database.transaction():\n            migrate(self.migrator.drop_column_default('person', 'first_name'))\n\n        if IS_MYSQL:\n            # MySQL, even though the column is NOT NULL, does not seem to be\n            # enforcing the constraint(?).\n            Person.create(last_name='Last2')\n            p_db = Person.get(Person.last_name == 'Last2')\n            self.assertEqual(p_db.first_name, '')\n        else:\n            with self.assertRaises(IntegrityError):\n                with self.database.transaction():\n                    Person.create(last_name='Last2')\n\n    def test_add_not_null(self):\n        self._create_people()\n\n        def addNotNull():\n            with self.database.transaction():\n                migrate(self.migrator.add_not_null('person', 'dob'))\n\n        # We cannot make the `dob` field not null because there is currently\n        # a null value there.\n        if self._exception_add_not_null:\n            with self.assertRaisesCtx((IntegrityError, InternalError)):\n                addNotNull()\n\n        (Person\n         .update(dob=datetime.date(2000, 1, 2))\n         .where(Person.dob >> None)\n         .execute())\n\n        # Now we can make the column not null.\n        addNotNull()\n\n        # And attempting to insert a null value results in an integrity error.\n        with self.database.transaction():\n            with self.assertRaisesCtx((IntegrityError, OperationalError)):\n                Person.create(\n                    first_name='Kirby',\n                    last_name='Snazebrauer',\n                    dob=None)\n\n    def test_drop_not_null(self):\n        self._create_people()\n        migrate(\n            self.migrator.drop_not_null('person', 'first_name'),\n            self.migrator.drop_not_null('person', 'last_name'))\n\n        p = Person.create(first_name=None, last_name=None)\n        query = (Person\n                 .select()\n                 .where(\n                     (Person.first_name >> None) &\n                     (Person.last_name >> None)))\n        self.assertEqual(query.count(), 1)\n\n    def test_modify_not_null_foreign_key(self):\n        user = User.create(id='charlie')\n        Page.create(name='null user')\n        Page.create(name='charlie', user=user)\n\n        def addNotNull():\n            with self.database.transaction():\n                migrate(self.migrator.add_not_null('page', 'user_id'))\n\n        if self._exception_add_not_null:\n            with self.assertRaisesCtx((IntegrityError, InternalError)):\n                addNotNull()\n\n        with self.database.transaction():\n            Page.update(user=user).where(Page.user.is_null()).execute()\n\n        addNotNull()\n\n        # And attempting to insert a null value results in an integrity error.\n        with self.database.transaction():\n            with self.assertRaisesCtx((OperationalError, IntegrityError)):\n                Page.create(\n                    name='fails',\n                    user=None)\n\n        # Now we will drop it.\n        with self.database.transaction():\n            migrate(self.migrator.drop_not_null('page', 'user_id'))\n\n        self.assertEqual(Page.select().where(Page.user.is_null()).count(), 0)\n        Page.create(name='succeeds', user=None)\n        self.assertEqual(Page.select().where(Page.user.is_null()).count(), 1)\n\n    def test_rename_table(self):\n        t1 = Tag.create(tag='t1')\n        t2 = Tag.create(tag='t2')\n\n        # Move the tag data into a new model/table.\n        class Tag_asdf(Tag):\n            pass\n        self.assertEqual(Tag_asdf._meta.table_name, 'tag_asdf')\n\n        # Drop the new table just to be safe.\n        Tag_asdf._schema.drop_all(True)\n\n        # Rename the tag table.\n        migrate(self.migrator.rename_table('tag', 'tag_asdf'))\n\n        # Verify the data was moved.\n        query = (Tag_asdf\n                 .select()\n                 .order_by(Tag_asdf.tag))\n        self.assertEqual([t.tag for t in query], ['t1', 't2'])\n\n        # Verify the old table is gone.\n        with self.database.transaction():\n            self.assertRaises(\n                DatabaseError,\n                Tag.create,\n                tag='t3')\n\n        self.database.execute_sql('drop table tag_asdf')\n\n    def test_add_index(self):\n        # Create a unique index on first and last names.\n        columns = ('first_name', 'last_name')\n        migrate(self.migrator.add_index('person', columns, True))\n\n        Person.create(first_name='first', last_name='last')\n        with self.database.transaction():\n            with self.assertRaisesCtx((IntegrityError, InternalError)):\n                Person.create(first_name='first', last_name='last')\n\n    def test_add_unique_column(self):\n        uf = CharField(default='', unique=True)\n\n        # Run the migration.\n        migrate(self.migrator.add_column('tag', 'unique_field', uf))\n\n        # Create a new tag model to represent the fields we added.\n        class NewTag(Model):\n            tag = CharField()\n            unique_field = uf\n\n            class Meta:\n                database = self.database\n                table_name = Tag._meta.table_name\n\n        NewTag.create(tag='t1', unique_field='u1')\n        NewTag.create(tag='t2', unique_field='u2')\n        with self.database.atomic():\n            self.assertRaises(IntegrityError, NewTag.create, tag='t3',\n                              unique_field='u1')\n\n    def test_drop_index(self):\n        # Create a unique index.\n        self.test_add_index()\n\n        # Now drop the unique index.\n        migrate(\n            self.migrator.drop_index('person', 'person_first_name_last_name'))\n\n        Person.create(first_name='first', last_name='last')\n        query = (Person\n                 .select()\n                 .where(\n                     (Person.first_name == 'first') &\n                     (Person.last_name == 'last')))\n        self.assertEqual(query.count(), 2)\n\n    def test_add_and_remove(self):\n        operations = []\n        field = CharField(default='foo')\n        for i in range(10):\n            operations.append(self.migrator.add_column('tag', 'foo', field))\n            operations.append(self.migrator.drop_column('tag', 'foo'))\n\n        migrate(*operations)\n        col_names = self.get_column_names('tag')\n        self.assertEqual(col_names, set(['id', 'tag']))\n\n    def test_multiple_operations(self):\n        self.database.execute_sql('drop table if exists person_baze;')\n        self.database.execute_sql('drop table if exists person_nugg;')\n        self._create_people()\n\n        field_n = CharField(null=True)\n        field_d = CharField(default='test')\n        operations = [\n            self.migrator.add_column('person', 'field_null', field_n),\n            self.migrator.drop_column('person', 'first_name'),\n            self.migrator.add_column('person', 'field_default', field_d),\n            self.migrator.rename_table('person', 'person_baze'),\n            self.migrator.rename_table('person_baze', 'person_nugg'),\n            self.migrator.rename_column('person_nugg', 'last_name', 'last'),\n            self.migrator.add_index('person_nugg', ('last',), True),\n        ]\n        migrate(*operations)\n\n        class PersonNugg(Model):\n            field_null = field_n\n            field_default = field_d\n            last = CharField()\n            dob = DateField(null=True)\n\n            class Meta:\n                database = self.database\n                table_name = 'person_nugg'\n\n        people = (PersonNugg\n                  .select(\n                      PersonNugg.field_null,\n                      PersonNugg.field_default,\n                      PersonNugg.last,\n                      PersonNugg.dob)\n                  .order_by(PersonNugg.last)\n                  .tuples())\n        expected = [\n            (None, 'test', 'Dog', datetime.date(2008, 6, 1)),\n            (None, 'test', 'Kitty', datetime.date(2011, 5, 1)),\n            (None, 'test', 'Leifer', None),\n        ]\n        self.assertEqual(list(people), expected)\n\n        with self.database.transaction():\n            self.assertRaises(\n                IntegrityError,\n                PersonNugg.create,\n                last='Leifer',\n                field_default='bazer')\n\n        self.database.execute_sql('drop table person_nugg;')\n\n    def test_add_foreign_key(self):\n        if hasattr(Person, 'newtag_set'):\n            delattr(Person, 'newtag_set')\n\n        # Ensure no foreign keys are present at the beginning of the test.\n        self.assertEqual(self.database.get_foreign_keys('tag'), [])\n\n        field = ForeignKeyField(Person, field=Person.id, null=True)\n        migrate(self.migrator.add_column('tag', 'person_id', field))\n\n        class NewTag(Tag):\n            person = field\n\n            class Meta:\n                table_name = 'tag'\n\n        p = Person.create(first_name='First', last_name='Last')\n        t1 = NewTag.create(tag='t1', person=p)\n        t2 = NewTag.create(tag='t2')\n\n        t1_db = NewTag.get(NewTag.tag == 't1')\n        self.assertEqual(t1_db.person, p)\n\n        t2_db = NewTag.get(NewTag.tag == 't2')\n        self.assertIsNone(t2_db.person)\n\n        foreign_keys = self.database.get_foreign_keys('tag')\n        self.assertEqual(len(foreign_keys), 1)\n        foreign_key = foreign_keys[0]\n        self.assertEqual(foreign_key.column, 'person_id')\n        self.assertEqual(foreign_key.dest_column, 'id')\n        self.assertEqual(foreign_key.dest_table, 'person')\n\n    def test_drop_foreign_key(self):\n        kw = {'legacy': True} if IS_SQLITE else {}\n        migrate(self.migrator.drop_column('page', 'user_id', **kw))\n        columns = self.database.get_columns('page')\n        self.assertEqual(\n            sorted(column.name for column in columns),\n            ['id', 'name'])\n        self.assertEqual(self.database.get_foreign_keys('page'), [])\n\n    def test_rename_foreign_key(self):\n        migrate(self.migrator.rename_column('page', 'user_id', 'huey_id'))\n        columns = self.database.get_columns('page')\n        self.assertEqual(\n            sorted(column.name for column in columns),\n            ['huey_id', 'id', 'name'])\n\n        foreign_keys = self.database.get_foreign_keys('page')\n        self.assertEqual(len(foreign_keys), 1)\n        foreign_key = foreign_keys[0]\n        self.assertEqual(foreign_key.column, 'huey_id')\n        self.assertEqual(foreign_key.dest_column, 'id')\n        self.assertEqual(foreign_key.dest_table, 'users')\n\n    def test_rename_unique_foreign_key(self):\n        migrate(self.migrator.rename_column('session', 'user_id', 'huey_id'))\n        columns = self.database.get_columns('session')\n        self.assertEqual(\n            sorted(column.name for column in columns),\n            ['huey_id', 'id', 'updated_at'])\n\n        foreign_keys = self.database.get_foreign_keys('session')\n        self.assertEqual(len(foreign_keys), 1)\n        foreign_key = foreign_keys[0]\n        self.assertEqual(foreign_key.column, 'huey_id')\n        self.assertEqual(foreign_key.dest_column, 'id')\n        self.assertEqual(foreign_key.dest_table, 'users')\n\n    @requires_pglike\n    @requires_models(Tag)\n    def test_add_column_with_index_type(self):\n        from playhouse.postgres_ext import BinaryJSONField\n        self.reset_sql_history()\n        field = BinaryJSONField(default=dict, index=True, null=True)\n        migrate(self.migrator.add_column('tag', 'metadata', field))\n        queries = [x.msg for x in self.history]\n        self.assertEqual(queries, [\n            ('ALTER TABLE \"tag\" ADD COLUMN \"metadata\" JSONB', []),\n            ('CREATE INDEX \"tag_metadata\" ON \"tag\" USING GIN (\"metadata\")',\n             []),\n        ])\n\n    @skip_if(IS_CRDB, 'crdb is still finnicky about changing types.')\n    def test_alter_column_type(self):\n        # Convert varchar to text.\n        field = TextField()\n        migrate(self.migrator.alter_column_type('tag', 'tag', field))\n        _, tag = self.database.get_columns('tag')\n        # name, type, null?, primary-key?, table, default.\n        data_type = 'TEXT' if IS_SQLITE else 'text'\n        self.assertEqual(tag, ('tag', data_type, False, False, 'tag', None))\n\n        # Convert date to datetime.\n        field = DateTimeField()\n        migrate(self.migrator.alter_column_type('person', 'dob', field))\n        _, _, _, dob = self.database.get_columns('person')\n        if IS_POSTGRESQL or IS_CRDB:\n            self.assertTrue(dob.data_type.startswith('timestamp'))\n        else:\n            self.assertEqual(dob.data_type.lower(), 'datetime')\n\n        # Convert text to integer.\n        field = IntegerField()\n        cast = '(tag::integer)' if IS_POSTGRESQL or IS_CRDB else None\n        migrate(self.migrator.alter_column_type('tag', 'tag', field, cast))\n        _, tag = self.database.get_columns('tag')\n        if IS_SQLITE:\n            data_type = 'INTEGER'\n        elif IS_MYSQL:\n            data_type = 'int'\n        else:\n            data_type = 'integer'\n        self.assertEqual(tag, ('tag', data_type, False, False, 'tag', None))\n\n    @requires_sqlite\n    def test_valid_column_required(self):\n        self.assertRaises(\n            (OperationalError, ValueError),\n            migrate,\n            self.migrator.drop_column('page', 'column_does_not_exist'))\n\n        self.assertRaises(\n            (OperationalError, ValueError),\n            migrate,\n            self.migrator.rename_column('page', 'xx', 'yy'))\n\n    @requires_sqlite\n    @requires_models(IndexModel)\n    def test_table_case_insensitive(self):\n        migrate(self.migrator.drop_column('PaGe', 'name', legacy=True))\n        column_names = self.get_column_names('page')\n        self.assertEqual(column_names, set(['id', 'user_id']))\n\n        testing_field = CharField(default='xx')\n        migrate(self.migrator.add_column('pAGE', 'testing', testing_field))\n        column_names = self.get_column_names('page')\n        self.assertEqual(column_names, set(['id', 'user_id', 'testing']))\n\n        migrate(self.migrator.drop_column('indeX_mOdel', 'first_name',\n                                          legacy=True))\n        indexes = self.migrator.database.get_indexes('index_model')\n        self.assertEqual(len(indexes), 1)\n        self.assertEqual(indexes[0].name, 'index_model_data')\n\n    @requires_sqlite\n    @requires_models(IndexModel)\n    def test_add_column_indexed_table(self):\n        # Ensure that columns can be added to tables that have indexes.\n        field = CharField(default='')\n        migrate(self.migrator.add_column('index_model', 'foo', field))\n\n        db = self.migrator.database\n        columns = db.get_columns('index_model')\n        self.assertEqual(sorted(column.name for column in columns),\n                         ['data', 'first_name', 'foo', 'id', 'last_name'])\n\n        indexes = db.get_indexes('index_model')\n        self.assertEqual(\n            sorted((index.name, index.columns) for index in indexes),\n            [('index_model_data', ['data']),\n             ('index_model_first_name_last_name',\n              ['first_name', 'last_name'])])\n\n    @requires_sqlite\n    def test_rename_column_to_table_name(self):\n        db = self.migrator.database\n        columns = lambda: sorted(col.name for col in db.get_columns('page'))\n        indexes = lambda: sorted((idx.name, idx.columns)\n                                 for idx in db.get_indexes('page'))\n\n        orig_columns = columns()\n        orig_indexes = indexes()\n\n        # Rename \"page\".\"name\" to \"page\".\"page\".\n        migrate(self.migrator.rename_column('page', 'name', 'page'))\n\n        # Ensure that the index on \"name\" is preserved, and that the index on\n        # the user_id foreign key is also preserved.\n        self.assertEqual(columns(),  ['id', 'page', 'user_id'])\n        self.assertEqual(indexes(), [\n            ('page_name', ['page']),\n            ('page_user_id', ['user_id'])])\n\n        # Revert the operation and verify\n        migrate(self.migrator.rename_column('page', 'page', 'name'))\n        self.assertEqual(columns(),  orig_columns)\n        self.assertEqual(indexes(), orig_indexes)\n\n    @requires_sqlite\n    @requires_models(Category)\n    def test_add_fk_with_constraints(self):\n        self.reset_sql_history()\n        field = ForeignKeyField(Category, Category.id, backref='children',\n                                null=True, on_delete='SET NULL')\n        migrate(self.migrator.add_column(\n            Category._meta.table_name,\n            'parent_id',\n            field))\n        queries = [x.msg for x in self.history]\n        self.assertEqual(queries, [\n            ('ALTER TABLE \"category\" ADD COLUMN \"parent_id\" '\n             'INTEGER REFERENCES \"category\" (\"id\") ON DELETE SET NULL', []),\n            ('CREATE INDEX \"category_parent_id\" ON \"category\" (\"parent_id\")',\n             []),\n        ])\n\n    @requires_sqlite\n    @requires_models(IndexModel)\n    def test_index_preservation(self):\n        self.reset_sql_history()\n        migrate(self.migrator.rename_column(\n            'index_model',\n            'first_name',\n            'first',\n            legacy=True))\n\n        queries = [x.msg for x in self.history]\n        self.assertEqual(queries, [\n            # Get all the columns.\n            ('PRAGMA \"main\".table_info(\"index_model\")', None),\n\n            # Get the table definition.\n            ('select name, sql from sqlite_master '\n             'where type=? and LOWER(name)=?',\n             ['table', 'index_model']),\n\n            # Get the indexes and indexed columns for the table.\n            ('SELECT name, sql FROM \"main\".sqlite_master '\n             'WHERE tbl_name = ? AND type = ? ORDER BY name',\n             ('index_model', 'index')),\n            ('PRAGMA \"main\".index_list(\"index_model\")', None),\n            ('PRAGMA \"main\".index_info(\"index_model_data\")', None),\n            ('PRAGMA \"main\".index_info(\"index_model_first_name_last_name\")',\n             None),\n\n            # Drop any temporary table, if it exists.\n            ('DROP TABLE IF EXISTS \"index_model__tmp__\"', []),\n\n            # Create a temporary table with the renamed column.\n            ('CREATE TABLE \"index_model__tmp__\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"first\" VARCHAR(255) NOT NULL, '\n             '\"last_name\" VARCHAR(255) NOT NULL, '\n             '\"data\" INTEGER NOT NULL)', []),\n\n            # Copy data from original table into temporary table.\n            ('INSERT INTO \"index_model__tmp__\" '\n             '(\"id\", \"first\", \"last_name\", \"data\") '\n             'SELECT \"id\", \"first_name\", \"last_name\", \"data\" '\n             'FROM \"index_model\"', []),\n\n            # Drop the original table.\n            ('DROP TABLE \"index_model\"', []),\n\n            # Rename the temporary table, replacing the original.\n            ('ALTER TABLE \"index_model__tmp__\" RENAME TO \"index_model\"', []),\n\n            # Re-create the indexes.\n            ('CREATE UNIQUE INDEX \"index_model_data\" '\n             'ON \"index_model\" (\"data\")', []),\n            ('CREATE UNIQUE INDEX \"index_model_first_name_last_name\" '\n             'ON \"index_model\" (\"first\", \"last_name\")', [])\n        ])\n\n    @requires_sqlite\n    @requires_models(User, Page)\n    def test_modify_fk_constraint(self):\n        self.reset_sql_history()\n        new_fk = ForeignKeyField(User, User.id, null=True, on_delete='CASCADE')\n        migrate(\n            self.migrator.drop_column('page', 'user_id', legacy=True),\n            self.migrator.add_column('page', 'user_id', new_fk))\n\n        queries = [x.msg for x in self.history]\n        self.assertEqual(queries, [\n            # Get all columns for table.\n            ('PRAGMA \"main\".table_info(\"page\")', None),\n\n            # Get the SQL used to generate the table and indexes.\n            ('select name, sql from sqlite_master '\n             'where type=? and LOWER(name)=?', ['table', 'page']),\n            ('SELECT name, sql FROM \"main\".sqlite_master '\n             'WHERE tbl_name = ? AND type = ? ORDER BY name',\n             ('page', 'index')),\n\n            # Get the indexes and indexed columns for the table.\n            ('PRAGMA \"main\".index_list(\"page\")', None),\n            ('PRAGMA \"main\".index_info(\"page_name\")', None),\n            ('PRAGMA \"main\".index_info(\"page_user_id\")', None),\n            #('PRAGMA \"main\".foreign_key_list(\"page\")', None),\n\n            # Clear out a temp table and create it w/o the user_id FK.\n            ('DROP TABLE IF EXISTS \"page__tmp__\"', []),\n            ('CREATE TABLE \"page__tmp__\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, \"name\" VARCHAR(100))', []),\n\n            # Copy data into the temp table, drop the original and rename\n            # the temp -> original. Recreate index(es).\n            ('INSERT INTO \"page__tmp__\" (\"id\", \"name\") '\n             'SELECT \"id\", \"name\" FROM \"page\"', []),\n            ('DROP TABLE \"page\"', []),\n            ('ALTER TABLE \"page__tmp__\" RENAME TO \"page\"', []),\n            ('CREATE UNIQUE INDEX \"page_name\" ON \"page\" (\"name\")', []),\n\n            # Add new foreign-key field with appropriate constraint.\n            ('ALTER TABLE \"page\" ADD COLUMN \"user_id\" VARCHAR(20) '\n             'REFERENCES \"users\" (\"id\") ON DELETE CASCADE', []),\n            ('CREATE INDEX \"page_user_id\" ON \"page\" (\"user_id\")', []),\n        ])\n\n        self.database.pragma('foreign_keys', 1)\n        huey = User.create(id='huey')\n        huey_page = Page.create(user=huey, name='huey page')\n        self.assertEqual(Page.select().count(), 1)\n\n        # Deleting the user will cascade to the associated page.\n        User.delete().where(User.id == 'huey').execute()\n        self.assertEqual(Page.select().count(), 0)\n\n    def test_make_index_name(self):\n        self.assertEqual(make_index_name('table', ['column']), 'table_column')\n\n    def test_make_index_name_long(self):\n        columns = [\n            'very_long_column_name_number_1',\n            'very_long_column_name_number_2',\n            'very_long_column_name_number_3',\n            'very_long_column_name_number_4'\n        ]\n        name = make_index_name('very_long_table_name', columns)\n        self.assertEqual(len(name), 64)\n\n\nclass BadNames(TestModel):\n    primary_data = TextField()\n    foreign_data = TextField()\n    data = TextField()\n\n    class Meta:\n        constraints = [\n            SQL('CONSTRAINT const1 UNIQUE (primary_data)'),\n            SQL('CONSTRAINT const2 UNIQUE (foreign_data)')]\n\n\nclass HasChecks(TestModel):\n    key = TextField()\n    value = IntegerField()\n    class Meta:\n        constraints = [\n            SQL(\"CHECK (key != '')\"),\n            SQL('CHECK (value > 0)')]\n\n\nclass TestSqliteColumnNameRegression(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [BadNames, HasChecks]\n\n    def test_sqlite_check_constraints(self):\n        HasChecks.create(key='k1', value=1)\n\n        migrator = SchemaMigrator.from_database(self.database)\n        extra = TextField(default='')\n        migrate(migrator.add_column('has_checks', 'extra', extra))\n\n        columns = self.database.get_columns('has_checks')\n        self.assertEqual([c.name for c in columns],\n                         ['id', 'key', 'value', 'extra'])\n\n        HC = Table('has_checks', ('id', 'key', 'value', 'extra'))\n        HC = HC.bind(self.database)\n\n        # Sanity-check: ensure we can create a new row.\n        data = {'key': 'k2', 'value': 2, 'extra': 'x2'}\n        self.assertTrue(HC.insert(data).execute())\n\n        # Check constraints preserved.\n        data = {'key': 'k0', 'value': 0, 'extra': 'x0'}\n        self.assertRaises(IntegrityError, HC.insert(data).execute)\n\n        data = {'key': '', 'value': 3, 'extra': 'x3'}\n        self.assertRaises(IntegrityError, HC.insert(data).execute)\n\n    def test_sqlite_column_name_constraint_regression(self):\n        BadNames.create(primary_data='pd', foreign_data='fd', data='d')\n\n        migrator = SchemaMigrator.from_database(self.database)\n        new_data = TextField(default='foo')\n        migrate(migrator.add_column('bad_names', 'new_data', new_data),\n                migrator.drop_column('bad_names', 'data'))\n\n        columns = self.database.get_columns('bad_names')\n        column_names = [column.name for column in columns]\n        self.assertEqual(column_names, ['id', 'primary_data', 'foreign_data',\n                                        'new_data'])\n\n        BNT = Table('bad_names', ('id', 'primary_data', 'foreign_data',\n                                  'new_data')).bind(self.database)\n        self.assertEqual([row for row in BNT.select()], [{\n            'id': 1,\n            'primary_data': 'pd',\n            'foreign_data': 'fd',\n            'new_data': 'foo'}])\n\n        # Verify constraints were carried over.\n        data = {'primary_data': 'pd', 'foreign_data': 'xx', 'new_data': 'd'}\n        self.assertRaises(IntegrityError, BNT.insert(data).execute)\n\n        data.update(primary_data='px', foreign_data='fd')\n        self.assertRaises(IntegrityError, BNT.insert(data).execute)\n\n        data.update(foreign_data='fx')\n        self.assertTrue(BNT.insert(data).execute())\n"
  },
  {
    "path": "tests/model_save.py",
    "content": "from peewee import *\n\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import requires_pglike\n\n\nclass T1(TestModel):\n    pk = AutoField()\n    value = IntegerField()\n\nclass T2(TestModel):\n    pk = IntegerField(constraints=[SQL('DEFAULT 3')], primary_key=True)\n    value = IntegerField()\n\nclass T3(TestModel):\n    pk = IntegerField(primary_key=True)\n    value = IntegerField()\n\nclass T4(TestModel):\n    pk1 = IntegerField()\n    pk2 = IntegerField()\n    value = IntegerField()\n    class Meta:\n        primary_key = CompositeKey('pk1', 'pk2')\n\nclass T5(TestModel):\n    val = IntegerField(null=True)\n\n\nclass TestPrimaryKeySaveHandling(ModelTestCase):\n    requires = [T1, T2, T3, T4]\n\n    def test_auto_field(self):\n        # AutoField will be inserted if the PK is not set, after which the new\n        # ID will be populated.\n        t11 = T1(value=1)\n        self.assertEqual(t11.save(), 1)\n        self.assertTrue(t11.pk is not None)\n\n        # Calling save() a second time will issue an update.\n        t11.value = 100\n        self.assertEqual(t11.save(), 1)\n\n        # Verify the record was updated.\n        t11_db = T1[t11.pk]\n        self.assertEqual(t11_db.value, 100)\n\n        # We can explicitly specify the value of an auto-incrementing\n        # primary-key, but we must be sure to call save(force_insert=True),\n        # otherwise peewee will attempt to do an update.\n        t12 = T1(pk=1337, value=2)\n        self.assertEqual(t12.save(), 0)\n        self.assertEqual(T1.select().count(), 1)\n        self.assertEqual(t12.save(force_insert=True), 1)\n\n        # Attempting to force-insert an already-existing PK will fail with an\n        # integrity error.\n        with self.database.atomic():\n            with self.assertRaises(IntegrityError):\n                t12.value = 3\n                t12.save(force_insert=True)\n\n        query = T1.select().order_by(T1.value).tuples()\n        self.assertEqual(list(query), [(1337, 2), (t11.pk, 100)])\n\n    @requires_pglike\n    def test_server_default_pk(self):\n        # The new value of the primary-key will be returned to us, since\n        # postgres supports RETURNING.\n        t2 = T2(value=1)\n        self.assertEqual(t2.save(), 1)\n        self.assertEqual(t2.pk, 3)\n\n        # Saving after the PK is set will issue an update.\n        t2.value = 100\n        self.assertEqual(t2.save(), 1)\n\n        t2_db = T2[3]\n        self.assertEqual(t2_db.value, 100)\n\n        # If we just set the pk and try to save, peewee issues an update which\n        # doesn't have any effect.\n        t22 = T2(pk=2, value=20)\n        self.assertEqual(t22.save(), 0)\n        self.assertEqual(T2.select().count(), 1)\n\n        # We can force-insert the value we specify explicitly.\n        self.assertEqual(t22.save(force_insert=True), 1)\n        self.assertEqual(T2[2].value, 20)\n\n    def test_integer_field_pk(self):\n        # For a non-auto-incrementing primary key, we have to use force_insert.\n        t3 = T3(pk=2, value=1)\n        self.assertEqual(t3.save(), 0)  # Oops, attempts to do an update.\n        self.assertEqual(T3.select().count(), 0)\n\n        # Force to be an insert.\n        self.assertEqual(t3.save(force_insert=True), 1)\n\n        # Now we can update the value and call save() to issue an update.\n        t3.value = 100\n        self.assertEqual(t3.save(), 1)\n\n        # Verify data is correct.\n        t3_db = T3[2]\n        self.assertEqual(t3_db.value, 100)\n\n    def test_composite_pk(self):\n        t4 = T4(pk1=1, pk2=2, value=10)\n\n        # Will attempt to do an update on non-existant rows.\n        self.assertEqual(t4.save(), 0)\n        self.assertEqual(t4.save(force_insert=True), 1)\n\n        # Modifying part of the composite PK and attempt an update will fail.\n        t4.pk2 = 3\n        t4.value = 30\n        self.assertEqual(t4.save(), 0)\n\n        t4.pk2 = 2\n        self.assertEqual(t4.save(), 1)\n\n        t4_db = T4[1, 2]\n        self.assertEqual(t4_db.value, 30)\n\n    @requires_pglike\n    def test_returning_object(self):\n        query = T2.insert(value=10).returning(T2).objects()\n        t2_db, = list(query)\n        self.assertEqual(t2_db.pk, 3)\n        self.assertEqual(t2_db.value, 10)\n\n\nclass TestSaveNoData(ModelTestCase):\n    requires = [T5]\n\n    def test_save_no_data(self):\n        t5 = T5.create()\n        self.assertTrue(t5.id >= 1)\n\n        t5.val = 3\n        t5.save()\n\n        t5_db = T5.get(T5.id == t5.id)\n        self.assertEqual(t5_db.val, 3)\n\n        t5.val = None\n        t5.save()\n\n        t5_db = T5.get(T5.id == t5.id)\n        self.assertTrue(t5_db.val is None)\n\n    def test_save_no_data2(self):\n        t5 = T5.create()\n\n        t5_db = T5.get(T5.id == t5.id)\n        t5_db.save()\n\n        t5_db = T5.get(T5.id == t5.id)\n        self.assertTrue(t5_db.val is None)\n\n    def test_save_no_data3(self):\n        t5 = T5.create()\n        self.assertRaises(ValueError, t5.save)\n\n    def test_save_only_no_data(self):\n        t5 = T5.create(val=1)\n        t5.val = 2\n        self.assertRaises(ValueError, t5.save, only=[])\n        t5_db = T5.get(T5.id == t5.id)\n        self.assertEqual(t5_db.val, 1)\n"
  },
  {
    "path": "tests/model_sql.py",
    "content": "import datetime\n\nfrom peewee import *\nfrom peewee import Alias\nfrom peewee import Database\nfrom peewee import ModelIndex\n\nfrom .base import get_in_memory_db\nfrom .base import requires_pglike\nfrom .base import skip_if\nfrom .base import BaseTestCase\nfrom .base import IS_CRDB\nfrom .base import ModelDatabaseTestCase\nfrom .base import TestModel\nfrom .base import __sql__\nfrom .base_models import *\n\n\nclass CKM(TestModel):\n    category = CharField()\n    key = CharField()\n    value = IntegerField()\n    class Meta:\n        primary_key = CompositeKey('category', 'key')\n\n\nclass TestModelSQL(ModelDatabaseTestCase):\n    database = get_in_memory_db()\n    requires = [Category, CKM, Note, Person, Relationship, Sample, User, DfltM]\n\n    def test_select(self):\n        query = (Person\n                 .select(\n                     Person.first,\n                     Person.last,\n                     fn.COUNT(Note.id).alias('ct'))\n                 .join(Note)\n                 .where((Person.last == 'Leifer') & (Person.id < 4)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"first\", \"t1\".\"last\", COUNT(\"t2\".\"id\") AS \"ct\" '\n            'FROM \"person\" AS \"t1\" '\n            'INNER JOIN \"note\" AS \"t2\" ON (\"t2\".\"author_id\" = \"t1\".\"id\") '\n            'WHERE ('\n            '(\"t1\".\"last\" = ?) AND '\n            '(\"t1\".\"id\" < ?))'), ['Leifer', 4])\n\n    def test_reselect(self):\n        sql = 'SELECT \"t1\".\"name\", \"t1\".\"parent_id\" FROM \"category\" AS \"t1\"'\n\n        query = Category.select()\n        self.assertSQL(query, sql, [])\n\n        query2 = query.select()\n        self.assertSQL(query2, sql, [])\n\n        query = Category.select(Category.name, Category.parent)\n        self.assertSQL(query, sql, [])\n\n        query2 = query.select()\n        self.assertSQL(query2, 'SELECT  FROM \"category\" AS \"t1\"', [])\n\n        query = query2.select(Category.name)\n        self.assertSQL(query, 'SELECT \"t1\".\"name\" FROM \"category\" AS \"t1\"', [])\n\n    def test_select_extend(self):\n        query = Note.select()\n        ext = query.join(Person).select_extend(Person)\n        self.assertSQL(ext, (\n            'SELECT \"t1\".\"id\", \"t1\".\"author_id\", \"t1\".\"content\", \"t2\".\"id\", '\n            '\"t2\".\"first\", \"t2\".\"last\", \"t2\".\"dob\" '\n            'FROM \"note\" AS \"t1\" INNER JOIN \"person\" AS \"t2\" '\n            'ON (\"t1\".\"author_id\" = \"t2\".\"id\")'), [])\n\n    def test_selected_columns(self):\n        query = (Person\n                 .select(\n                     Person.first,\n                     Person.last,\n                     fn.COUNT(Note.id).alias('ct'))\n                 .join(Note))\n        f_first, f_last, f_ct = query.selected_columns\n        self.assertEqual(f_first.name, 'first')\n        self.assertTrue(f_first.model is Person)\n        self.assertEqual(f_last.name, 'last')\n        self.assertTrue(f_last.model is Person)\n        self.assertTrue(isinstance(f_ct, Alias))\n        f_ct = f_ct.unwrap()\n        self.assertEqual(f_ct.name, 'COUNT')\n        f_nid, = f_ct.arguments\n        self.assertEqual(f_nid.name, 'id')\n        self.assertTrue(f_nid.model is Note)\n\n        query.selected_columns = (Person.first,)\n        f_first, = query.selected_columns\n        self.assertEqual(f_first.name, 'first')\n        self.assertTrue(f_first.model is Person)\n\n    def test_where_coerce(self):\n        query = Person.select(Person.last).where(Person.id == '1337')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"last\" FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" = ?)'), [1337])\n\n        query = Person.select(Person.last).where(Person.id < (Person.id - '5'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"last\" FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" < (\"t1\".\"id\" - ?))'), [5])\n\n        query = Person.select(Person.last).where(Person.first == b'foo')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"last\" FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"first\" = ?)'), ['foo'])\n\n    def test_group_by(self):\n        query = (User\n                 .select(User, fn.COUNT(Tweet.id).alias('tweet_count'))\n                 .join(Tweet, JOIN.LEFT_OUTER)\n                 .group_by(User))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\", '\n            'COUNT(\"t2\".\"id\") AS \"tweet_count\" '\n            'FROM \"users\" AS \"t1\" '\n            'LEFT OUTER JOIN \"tweet\" AS \"t2\" ON (\"t2\".\"user_id\" = \"t1\".\"id\") '\n            'GROUP BY \"t1\".\"id\", \"t1\".\"username\"'), [])\n\n    def test_group_by_extend(self):\n        query = (User\n                 .select(User, fn.COUNT(Tweet.id).alias('tweet_count'))\n                 .join(Tweet, JOIN.LEFT_OUTER)\n                 .group_by_extend(User.id).group_by_extend(User.username))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\", '\n            'COUNT(\"t2\".\"id\") AS \"tweet_count\" '\n            'FROM \"users\" AS \"t1\" '\n            'LEFT OUTER JOIN \"tweet\" AS \"t2\" ON (\"t2\".\"user_id\" = \"t1\".\"id\") '\n            'GROUP BY \"t1\".\"id\", \"t1\".\"username\"'), [])\n\n    def test_order_by(self):\n        query = (User\n                 .select()\n                 .order_by(User.username.desc(), User.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'ORDER BY \"t1\".\"username\" DESC, \"t1\".\"id\"'), [])\n\n    def test_order_by_extend(self):\n        query = (User\n                 .select()\n                 .order_by_extend(User.username.desc())\n                 .order_by_extend(User.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'ORDER BY \"t1\".\"username\" DESC, \"t1\".\"id\"'), [])\n\n    def test_paginate(self):\n        # Get the first page, default is limit of 20.\n        query = User.select().paginate(1)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'LIMIT ? OFFSET ?'), [20, 0])\n\n        # Page 3 contains rows 31-45.\n        query = User.select().paginate(3, 15)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'LIMIT ? OFFSET ?'), [15, 30])\n\n    def test_subquery_correction(self):\n        users = User.select().where(User.username.in_(['foo', 'bar']))\n        query = Tweet.select().where(Tweet.user.in_(users))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"user_id\", \"t1\".\"content\", '\n            '\"t1\".\"timestamp\" '\n            'FROM \"tweet\" AS \"t1\" '\n            'WHERE (\"t1\".\"user_id\" IN ('\n            'SELECT \"t2\".\"id\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"username\" IN (?, ?))))'), ['foo', 'bar'])\n\n    def test_value_flattening(self):\n        sql = ('SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n               'WHERE (\"t1\".\"username\" IN (?, ?))')\n        expected = (sql, ['foo', 'bar'])\n\n        users = User.select().where(User.username.in_(['foo', 'bar']))\n        self.assertSQL(users, *expected)\n\n        users = User.select().where(User.username.in_(('foo', 'bar')))\n        self.assertSQL(users, *expected)\n\n        users = User.select().where(User.username.in_(set(['foo', 'bar'])))\n        # Sets are unordered so params may be in either order:\n        sql, params = __sql__(users)\n        self.assertEqual(sql, expected[0])\n        self.assertTrue(params in (['foo', 'bar'], ['bar', 'foo']))\n\n    def test_model_select_from(self):\n        inner = (User\n                 .select(User.id, User.username)\n                 .where(User.username == 'x'))\n        query = inner.select_from(inner.c.username)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\" FROM ('\n            'SELECT \"t2\".\"id\", \"t2\".\"username\" '\n            'FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"username\" = ?)) AS \"t1\"'), ['x'])\n\n    def test_join_ctx(self):\n        query = Tweet.select(Tweet.id).join(Favorite).switch(Tweet).join(User)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"favorite\" AS \"t2\" ON (\"t2\".\"tweet_id\" = \"t1\".\"id\") '\n            'INNER JOIN \"users\" AS \"t3\" ON (\"t1\".\"user_id\" = \"t3\".\"id\")'), [])\n\n        query = Tweet.select(Tweet.id).join(User).switch(Tweet).join(Favorite)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n            'INNER JOIN \"favorite\" AS \"t3\" ON (\"t3\".\"tweet_id\" = \"t1\".\"id\")'),\n            [])\n\n        query = Tweet.select(Tweet.id).left_outer_join(Favorite).switch(Tweet).left_outer_join(User)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" '\n            'LEFT OUTER JOIN \"favorite\" AS \"t2\" ON (\"t2\".\"tweet_id\" = \"t1\".\"id\") '\n            'LEFT OUTER JOIN \"users\" AS \"t3\" ON (\"t1\".\"user_id\" = \"t3\".\"id\")'), [])\n\n        query = Tweet.select(Tweet.id).left_outer_join(User).switch(Tweet).left_outer_join(Favorite)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" '\n            'LEFT OUTER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n            'LEFT OUTER JOIN \"favorite\" AS \"t3\" ON (\"t3\".\"tweet_id\" = \"t1\".\"id\")'),\n            [])\n\n    def test_model_alias(self):\n        TA = Tweet.alias()\n        query = (User\n                 .select(User, fn.COUNT(TA.id).alias('tc'))\n                 .join(TA, on=(User.id == TA.user))\n                 .group_by(User))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\", COUNT(\"t2\".\"id\") AS \"tc\" '\n            'FROM \"users\" AS \"t1\" '\n            'INNER JOIN \"tweet\" AS \"t2\" ON (\"t1\".\"id\" = \"t2\".\"user_id\") '\n            'GROUP BY \"t1\".\"id\", \"t1\".\"username\"'), [])\n\n    def test_model_alias_with_schema(self):\n        class Note(TestModel):\n            content = TextField()\n            class Meta:\n                schema = 'notes'\n\n        query = Note.select()\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"content\" '\n            'FROM \"notes\".\"note\" AS \"t1\"'), [])\n\n        query = Note.alias('na').select()\n        self.assertSQL(query, (\n            'SELECT \"na\".\"id\", \"na\".\"content\" '\n            'FROM \"notes\".\"note\" AS \"na\"'), [])\n\n    def test_model_alias_join_with_schema(self):\n        class Note(TestModel):\n            content = TextField()\n            class Meta:\n                schema = 'notes'\n\n        NA = Note.alias('na')\n        query = (Note\n                 .select(Note.content, NA.content)\n                 .join(NA, on=(NA.id == Note.id)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\", \"na\".\"content\" '\n            'FROM \"notes\".\"note\" AS \"t1\" '\n            'INNER JOIN \"notes\".\"note\" AS \"na\" '\n            'ON (\"na\".\"id\" = \"t1\".\"id\")'), [])\n\n    def test_filter_simple(self):\n        query = User.filter(username='huey')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" = ?)'), ['huey'])\n\n        query = User.filter(username='huey', id__gte=1, id__lt=5)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (((\"t1\".\"id\" >= ?) AND (\"t1\".\"id\" < ?)) AND '\n            '(\"t1\".\"username\" = ?))'), [1, 5, 'huey'])\n\n        query = User.filter(~DQ(id=1), username__in=('foo', 'bar'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (NOT (\"t1\".\"id\" = ?) AND (\"t1\".\"username\" IN (?, ?)))'),\n            [1, 'foo', 'bar'])\n\n        query = User.filter((DQ(id=1) | DQ(id=2)), username__in=('foo', 'bar'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (((\"t1\".\"id\" = ?) OR (\"t1\".\"id\" = ?)) AND '\n            '(\"t1\".\"username\" IN (?, ?)))'), [1, 2, 'foo', 'bar'])\n\n    def test_filter_expressions(self):\n        query = User.filter(\n            DQ(username__in=['huey', 'zaizee']) |\n            (DQ(id__gt=2) & DQ(id__lt=4)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" '\n            'FROM \"users\" AS \"t1\" '\n            'WHERE ((\"t1\".\"username\" IN (?, ?)) OR '\n            '((\"t1\".\"id\" > ?) AND (\"t1\".\"id\" < ?)))'),\n            ['huey', 'zaizee', 2, 4])\n\n    def test_filter_join(self):\n        query = Tweet.select(Tweet.content).filter(user__username='huey')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\" FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n            'WHERE (\"t2\".\"username\" = ?)'), ['huey'])\n\n        UA = User.alias('ua')\n        query = (Tweet\n                 .select(Tweet.content)\n                 .join(UA)\n                 .filter(ua__username='huey'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\" FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"ua\" ON (\"t1\".\"user_id\" = \"ua\".\"id\") '\n            'WHERE (\"ua\".\"username\" = ?)'), ['huey'])\n\n    def test_filter_with_or_across_joins(self):\n        query = (Tweet\n                 .select(Tweet.content)\n                 .filter(\n                     DQ(user__username='huey') |\n                     DQ(content__like='%hello%')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\" FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"t2\" '\n            'ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n            'WHERE ((\"t2\".\"username\" = ?) OR '\n            '(\"t1\".\"content\" LIKE ?))'), ['huey', '%hello%'])\n\n    def test_filter_join_combine_models(self):\n        query = (Tweet\n                 .select(Tweet.content)\n                 .filter(user__username='huey')\n                 .filter(DQ(user__id__gte=1) | DQ(id__lt=5)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\" FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n            'WHERE ((\"t2\".\"username\" = ?) AND '\n            '((\"t2\".\"id\" >= ?) OR (\"t1\".\"id\" < ?)))'), ['huey', 1, 5])\n\n    def test_mix_filter_methods(self):\n        query = (User\n                 .select(User, fn.COUNT(Tweet.id).alias('count'))\n                 .filter(username__in=('huey', 'zaizee'))\n                 .join(Tweet, JOIN.LEFT_OUTER)\n                 .group_by(User.id, User.username)\n                 .order_by(fn.COUNT(Tweet.id).desc()))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\", COUNT(\"t2\".\"id\") AS \"count\" '\n            'FROM \"users\" AS \"t1\" '\n            'LEFT OUTER JOIN \"tweet\" AS \"t2\" ON (\"t2\".\"user_id\" = \"t1\".\"id\") '\n            'WHERE (\"t1\".\"username\" IN (?, ?)) '\n            'GROUP BY \"t1\".\"id\", \"t1\".\"username\" '\n            'ORDER BY COUNT(\"t2\".\"id\") DESC'), ['huey', 'zaizee'])\n\n    def test_join_parent(self):\n        query = (Category\n                 .select()\n                 .where(Category.parent == 'test'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\", \"t1\".\"parent_id\" FROM \"category\" AS \"t1\" '\n            'WHERE (\"t1\".\"parent_id\" = ?)'), ['test'])\n\n        query = Category.filter(parent='test')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\", \"t1\".\"parent_id\" FROM \"category\" AS \"t1\" '\n            'WHERE (\"t1\".\"parent_id\" = ?)'), ['test'])\n\n    def test_cross_join(self):\n        class A(TestModel):\n            id = IntegerField(primary_key=True)\n        class B(TestModel):\n            id = IntegerField(primary_key=True)\n        query = (A\n                 .select(A.id.alias('aid'), B.id.alias('bid'))\n                 .join(B, JOIN.CROSS)\n                 .order_by(A.id, B.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" AS \"aid\", \"t2\".\"id\" AS \"bid\" '\n            'FROM \"a\" AS \"t1\" '\n            'CROSS JOIN \"b\" AS \"t2\" '\n            'ORDER BY \"t1\".\"id\", \"t2\".\"id\"'), [])\n\n    def test_join_expr(self):\n        class User(TestModel):\n            username = TextField(primary_key=True)\n        class Tweet(TestModel):\n            user = ForeignKeyField(User, backref='tweets')\n            content = TextField()\n\n        sql = ('SELECT \"t1\".\"id\", \"t1\".\"user_id\", \"t1\".\"content\", '\n               '\"t2\".\"username\" FROM \"tweet\" AS \"t1\" '\n               'INNER JOIN \"user\" AS \"t2\" '\n               'ON (\"t1\".\"user_id\" = \"t2\".\"username\")')\n\n        query = Tweet.select(Tweet, User).join(User)\n        self.assertSQL(query, sql, [])\n\n        query = (Tweet\n                 .select(Tweet, User)\n                 .join(User, on=(Tweet.user == User.username)))\n        self.assertSQL(query, sql, [])\n\n        join_expr = ((Tweet.user == User.username) & (Value(1) == 1))\n        query = Tweet.select().join(User, on=join_expr)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"user_id\", \"t1\".\"content\" '\n            'FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"user\" AS \"t2\" '\n            'ON ((\"t1\".\"user_id\" = \"t2\".\"username\") AND (? = ?))'), [1, 1])\n\n    def test_join_multiple_fks(self):\n        class A(TestModel):\n            name = TextField()\n        class B(TestModel):\n            name = TextField(primary_key=True)\n            a1 = ForeignKeyField(A, backref='b_set1')\n            a2 = ForeignKeyField(A, field=A.name, backref='b_set2')\n\n        A1 = A.alias('a1')\n        A2 = A.alias('a2')\n\n        sql = ('SELECT \"t1\".\"name\", \"t1\".\"a1_id\", \"t1\".\"a2_id\", '\n               '\"a1\".\"id\", \"a1\".\"name\", \"a2\".\"id\", \"a2\".\"name\" '\n               'FROM \"b\" AS \"t1\" '\n               'INNER JOIN \"a\" AS \"a1\" ON (\"t1\".\"a1_id\" = \"a1\".\"id\") '\n               'INNER JOIN \"a\" AS \"a2\" ON (\"t1\".\"a2_id\" = \"a2\".\"name\")')\n\n        query = (B.select(B, A1, A2)\n                 .join_from(B, A1, on=B.a1)\n                 .join_from(B, A2, on=B.a2))\n        self.assertSQL(query, sql, [])\n\n        query = (B.select(B, A1, A2)\n                 .join(A1, on=(B.a1 == A1.id)).switch(B)\n                 .join(A2, on=(B.a2 == A2.name)))\n        self.assertSQL(query, sql, [])\n\n        jx1 = (B.a1 == A1.id) & (Value(1) == 1)\n        jx2 = (Value(1) == 1) & (B.a2 == A2.name)\n        query = (B.select()\n                 .join(A1, on=jx1).switch(B)\n                 .join(A2, on=jx2))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\", \"t1\".\"a1_id\", \"t1\".\"a2_id\" '\n            'FROM \"b\" AS \"t1\" '\n            'INNER JOIN \"a\" AS \"a1\" '\n            'ON ((\"t1\".\"a1_id\" = \"a1\".\"id\") AND (? = ?)) '\n            'INNER JOIN \"a\" AS \"a2\" '\n            'ON ((? = ?) AND (\"t1\".\"a2_id\" = \"a2\".\"name\"))'), [1, 1, 1, 1])\n\n    def test_raw(self):\n        query = (Person\n                 .raw('SELECT first, last, dob FROM person '\n                      'WHERE first = ? AND substr(last, 1, 1) = ? '\n                      'ORDER BY last', 'huey', 'l'))\n        self.assertSQL(query, (\n            'SELECT first, last, dob FROM person '\n            'WHERE first = ? AND substr(last, 1, 1) = ? '\n            'ORDER BY last'), ['huey', 'l'])\n\n    def test_insert(self):\n        query = (Person\n                 .insert({Person.first: 'huey',\n                          Person.last: 'cat',\n                          Person.dob: datetime.date(2011, 1, 1)}))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"first\", \"last\", \"dob\") '\n            'VALUES (?, ?, ?)'), ['huey', 'cat', datetime.date(2011, 1, 1)])\n\n        query = (Note\n                 .insert({Note.author: Person(id=1337),\n                          Note.content: 'leet'}))\n        self.assertSQL(query, (\n            'INSERT INTO \"note\" (\"author_id\", \"content\") '\n            'VALUES (?, ?)'), [1337, 'leet'])\n\n        query = Person.insert(first='huey', last='cat')\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"first\", \"last\") VALUES (?, ?)'),\n            ['huey', 'cat'])\n\n    def test_replace(self):\n        query = (Person\n                 .replace({Person.first: 'huey',\n                           Person.last: 'cat'}))\n        self.assertSQL(query, (\n            'INSERT OR REPLACE INTO \"person\" (\"first\", \"last\") '\n            'VALUES (?, ?)'), ['huey', 'cat'])\n\n    def test_insert_many(self):\n        query = (Note\n                 .insert_many((\n                     {Note.author: Person(id=1), Note.content: 'note-1'},\n                     {Note.author: Person(id=2), Note.content: 'note-2'},\n                     {Note.author: Person(id=3), Note.content: 'note-3'})))\n        self.assertSQL(query, (\n            'INSERT INTO \"note\" (\"author_id\", \"content\") '\n            'VALUES (?, ?), (?, ?), (?, ?)'),\n            [1, 'note-1', 2, 'note-2', 3, 'note-3'])\n\n        query = (Note\n                 .insert_many((\n                     {'author': Person(id=1), 'content': 'note-1'},\n                     {'author': Person(id=2), 'content': 'note-2'})))\n        self.assertSQL(query, (\n            'INSERT INTO \"note\" (\"author_id\", \"content\") '\n            'VALUES (?, ?), (?, ?)'),\n            [1, 'note-1', 2, 'note-2'])\n\n    def test_insert_many_defaults(self):\n        # Verify fields are inferred and values are read correctly, when\n        # partial data is given and a field has default values.\n        s2 = {'counter': 2, 'value': 2.}\n        s3 = {'counter': 3}\n        self.assertSQL(Sample.insert_many([s2, s3]), (\n            'INSERT INTO \"sample\" (\"counter\", \"value\") VALUES (?, ?), (?, ?)'),\n            [2, 2., 3, 1.])\n\n        self.assertSQL(Sample.insert_many([s3, s2]), (\n            'INSERT INTO \"sample\" (\"counter\", \"value\") VALUES (?, ?), (?, ?)'),\n            [3, 1., 2, 2.])\n\n    def test_insert_many_defaults_nulls(self):\n        data = [\n            {'name': 'd1'},\n            {'name': 'd2', 'dflt1': 10},\n            {'name': 'd3', 'dflt2': 30},\n            {'name': 'd4', 'dfltn': 40}]\n        fields = [DfltM.name, DfltM.dflt1, DfltM.dflt2, DfltM.dfltn]\n        self.assertSQL(DfltM.insert_many(data, fields=fields), (\n            'INSERT INTO \"dflt_m\" (\"name\", \"dflt1\", \"dflt2\", \"dfltn\") VALUES '\n            '(?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?), (?, ?, ?, ?)'),\n            ['d1', 1, 2, None,\n             'd2', 10, 2, None,\n             'd3', 1, 30, None,\n             'd4', 1, 2, 40])\n\n    def test_insert_many_list_with_fields(self):\n        data = [(i,) for i in ('charlie', 'huey', 'zaizee')]\n        query = User.insert_many(data, fields=[User.username])\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"username\") VALUES (?), (?), (?)'),\n            ['charlie', 'huey', 'zaizee'])\n\n        # Use field name instead of field obj.\n        query = User.insert_many(data, fields=['username'])\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"username\") VALUES (?), (?), (?)'),\n            ['charlie', 'huey', 'zaizee'])\n\n    def test_insert_many_infer_fields(self):\n        data = [('f1', 'l1', '1980-01-01'),\n                ('f2', 'l2', '1980-02-02')]\n        self.assertSQL(Person.insert_many(data), (\n            'INSERT INTO \"person\" (\"first\", \"last\", \"dob\") '\n            'VALUES (?, ?, ?), (?, ?, ?)'),\n            ['f1', 'l1', datetime.date(1980, 1, 1),\n             'f2', 'l2', datetime.date(1980, 2, 2)])\n\n        # When primary key is not auto-increment, PKs are included.\n        data = [('c1', 'k1', 1), ('c2', 'k2', 2)]\n        self.assertSQL(CKM.insert_many(data), (\n            'INSERT INTO \"ckm\" (\"category\", \"key\", \"value\") '\n            'VALUES (?, ?, ?), (?, ?, ?)'), ['c1', 'k1', 1, 'c2', 'k2', 2])\n\n    def test_insert_query(self):\n        select = (Person\n                  .select(Person.id, Person.first)\n                  .where(Person.last == 'cat'))\n        query = Note.insert_from(select, (Note.author, Note.content))\n        self.assertSQL(query, ('INSERT INTO \"note\" (\"author_id\", \"content\") '\n                               'SELECT \"t1\".\"id\", \"t1\".\"first\" '\n                               'FROM \"person\" AS \"t1\" '\n                               'WHERE (\"t1\".\"last\" = ?)'), ['cat'])\n\n        query = Note.insert_from(select, ('author', 'content'))\n        self.assertSQL(query, ('INSERT INTO \"note\" (\"author_id\", \"content\") '\n                               'SELECT \"t1\".\"id\", \"t1\".\"first\" '\n                               'FROM \"person\" AS \"t1\" '\n                               'WHERE (\"t1\".\"last\" = ?)'), ['cat'])\n\n    def test_insert_returning(self):\n        class TestDB(Database):\n            returning_clause = True\n\n        class User(Model):\n            username = CharField()\n            class Meta:\n                database = TestDB(None)\n\n        query = User.insert({User.username: 'zaizee'})\n        self.assertSQL(query, (\n            'INSERT INTO \"user\" (\"username\") '\n            'VALUES (?) RETURNING \"user\".\"id\"'), ['zaizee'])\n\n        class Person(Model):\n            name = CharField()\n            ssn = CharField(primary_key=True)\n            class Meta:\n                database = TestDB(None)\n\n        query = Person.insert({Person.name: 'charlie', Person.ssn: '123'})\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"ssn\", \"name\") VALUES (?, ?) '\n            'RETURNING \"person\".\"ssn\"'), ['123', 'charlie'])\n\n        query = Person.insert({Person.name: 'huey'}).returning()\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?)'), ['huey'])\n\n        query = (Person\n                 .insert({Person.name: 'foo'})\n                 .returning(Person.ssn.alias('new_ssn')))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?) '\n            'RETURNING \"person\".\"ssn\" AS \"new_ssn\"'), ['foo'])\n\n    def test_insert_get_field_values(self):\n        class User(TestModel):\n            username = TextField(primary_key=True)\n            class Meta:\n                database = self.database\n\n        class Tweet(TestModel):\n            user = ForeignKeyField(User)\n            content = TextField()\n            class Meta:\n                database = self.database\n\n        queries = (\n            User.insert(username='a'),\n            User.insert({'username': 'a'}),\n            User.insert({User.username: 'a'}))\n        for query in queries:\n            self.assertSQL(query, ('INSERT INTO \"user\" (\"username\") '\n                                   'VALUES (?)'), ['a'])\n\n        # Verify that we can provide all kinds of combinations to the\n        # constructor to INSERT and it will map the parameters correctly\n        # without losing values.\n        a = User(username='a')\n        queries = (\n            Tweet.insert(user=a, content='ca'),\n            Tweet.insert({'user': a, 'content': 'ca'}),\n            Tweet.insert({Tweet.user: a, 'content': 'ca'}),\n            Tweet.insert({'user': a, Tweet.content: 'ca'}),\n            Tweet.insert({Tweet.user: a, Tweet.content: 'ca'}),\n            Tweet.insert({Tweet.user: a}, content='ca'),\n            Tweet.insert({Tweet.content: 'ca'}, user=a),\n            Tweet.insert({'user': a}, content='ca'),\n            Tweet.insert({'content': 'ca'}, user=a),\n\n            # Also test using the foreign-key descriptor and column name.\n            Tweet.insert({Tweet.user_id: a, Tweet.content: 'ca'}),\n            Tweet.insert(user_id=a, content='ca'),\n            Tweet.insert({'user_id': a, 'content': 'ca'}))\n\n        for query in queries:\n            self.assertSQL(query, ('INSERT INTO \"tweet\" (\"user_id\", \"content\")'\n                                   ' VALUES (?, ?)'), ['a', 'ca'])\n\n    def test_insert_many_get_field_values(self):\n        class User(TestModel):\n            username = TextField(primary_key=True)\n            class Meta:\n                database = self.database\n\n        class Tweet(TestModel):\n            user = ForeignKeyField(User)\n            content = TextField()\n            class Meta:\n                database = self.database\n\n        # Ensure we can handle any combination of insert-data key and field\n        # list value.\n        pairs = ((User.username, 'username'),\n                 ('username', User.username),\n                 ('username', 'username'),\n                 (User.username, User.username))\n\n        for dict_key, fields_key in pairs:\n            iq = User.insert_many([{dict_key: u} for u in 'abc'],\n                                  fields=[fields_key])\n            self.assertSQL(iq, (\n                'INSERT INTO \"user\" (\"username\") VALUES (?), (?), (?)'),\n                ['a', 'b', 'c'])\n\n        a, b = User(username='a'), User(username='b')\n        user_content = (\n            (a, 'ca1'),\n            (a, 'ca2'),\n            (b, 'cb1'),\n            ('a', 'ca3'))  # Specify user id directly.\n\n        # Ensure we can mix-and-match key type within insert-data.\n        pairs = (('user', 'content'),\n                 (Tweet.user, Tweet.content),\n                 (Tweet.user, 'content'),\n                 ('user', Tweet.content),\n                 ('user_id', 'content'),\n                 (Tweet.user_id, Tweet.content))\n\n        for ukey, ckey in pairs:\n            iq = Tweet.insert_many([{ukey: u, ckey: c}\n                                    for u, c in user_content])\n            self.assertSQL(iq, (\n                'INSERT INTO \"tweet\" (\"user_id\", \"content\") VALUES '\n                '(?, ?), (?, ?), (?, ?), (?, ?)'),\n                ['a', 'ca1', 'a', 'ca2', 'b', 'cb1', 'a', 'ca3'])\n\n    def test_insert_many_dict_and_list(self):\n        class R(TestModel):\n            k = TextField(column_name='key')\n            v = IntegerField(column_name='value', default=0)\n            class Meta:\n                database = self.database\n\n        data = (\n            {'k': 'k1', 'v': 1},\n            {R.k: 'k2', R.v: 2},\n            {'key': 'k3', 'value': 3},\n            ('k4', 4),\n            ('k5', '5'),  # Will be converted properly.\n            {R.k: 'k6', R.v: '6'},\n            {'key': 'k7', 'value': '7'},\n            {'k': 'kx'},\n            ('ky',))\n\n        param_str = ', '.join('(?, ?)' for _ in range(len(data)))\n        queries = (\n            R.insert_many(data),\n            R.insert_many(data, fields=[R.k, R.v]),\n            R.insert_many(data, fields=['k', 'v']))\n        for query in queries:\n            self.assertSQL(query, (\n                'INSERT INTO \"r\" (\"key\", \"value\") VALUES %s' % param_str),\n                ['k1', 1, 'k2', 2, 'k3', 3, 'k4', 4, 'k5', 5, 'k6', 6,\n                 'k7', 7, 'kx', 0, 'ky', 0])\n\n    def test_insert_modelalias(self):\n        UA = User.alias('ua')\n        self.assertSQL(UA.insert({UA.username: 'huey'}), (\n            'INSERT INTO \"users\" (\"username\") VALUES (?)'), ['huey'])\n        self.assertSQL(UA.insert(username='huey'), (\n            'INSERT INTO \"users\" (\"username\") VALUES (?)'), ['huey'])\n\n    def test_update(self):\n        class Stat(TestModel):\n            url = TextField()\n            count = IntegerField()\n            timestamp = TimestampField(utc=True)\n\n        query = (Stat\n                 .update({Stat.count: Stat.count + 1,\n                          Stat.timestamp: datetime.datetime(2017, 1, 1)})\n                 .where(Stat.url == '/peewee'))\n        self.assertSQL(query, (\n            'UPDATE \"stat\" SET \"count\" = (\"stat\".\"count\" + ?), '\n            '\"timestamp\" = ? '\n            'WHERE (\"stat\".\"url\" = ?)'),\n            [1, 1483228800, '/peewee'])\n\n        query = (Stat\n                 .update(count=Stat.count + 1)\n                 .where(Stat.url == '/peewee'))\n        self.assertSQL(query, (\n            'UPDATE \"stat\" SET \"count\" = (\"stat\".\"count\" + ?) '\n            'WHERE (\"stat\".\"url\" = ?)'),\n            [1, '/peewee'])\n\n    def test_update_subquery(self):\n        class U(TestModel):\n            username = TextField()\n            flood_count = IntegerField()\n\n        class T(TestModel):\n            user = ForeignKeyField(U)\n\n        ctq = T.select(fn.COUNT(T.id) / 100).where(T.user == U.id)\n        subq = (T\n                .select(T.user)\n                .group_by(T.user)\n                .having(fn.COUNT(T.id) > 100))\n        query = (U\n                 .update({U.flood_count: ctq})\n                 .where(U.id.in_(subq)))\n        self.assertSQL(query, (\n            'UPDATE \"u\" SET \"flood_count\" = ('\n            'SELECT (COUNT(\"t1\".\"id\") / ?) FROM \"t\" AS \"t1\" '\n            'WHERE (\"t1\".\"user_id\" = \"u\".\"id\")) '\n            'WHERE (\"u\".\"id\" IN ('\n            'SELECT \"t1\".\"user_id\" FROM \"t\" AS \"t1\" '\n            'GROUP BY \"t1\".\"user_id\" '\n            'HAVING (COUNT(\"t1\".\"id\") > ?)))'), [100, 100])\n\n    def test_update_from(self):\n        class SalesPerson(TestModel):\n            first = TextField()\n            last = TextField()\n\n        class Account(TestModel):\n            contact_first = TextField()\n            contact_last = TextField()\n            sales = ForeignKeyField(SalesPerson)\n\n        query = (Account\n                 .update(contact_first=SalesPerson.first,\n                         contact_last=SalesPerson.last)\n                 .from_(SalesPerson)\n                 .where(Account.sales == SalesPerson.id))\n        self.assertSQL(query, (\n            'UPDATE \"account\" SET '\n            '\"contact_first\" = \"t1\".\"first\", '\n            '\"contact_last\" = \"t1\".\"last\" '\n            'FROM \"sales_person\" AS \"t1\" '\n            'WHERE (\"account\".\"sales_id\" = \"t1\".\"id\")'), [])\n\n        query = (User\n                 .update({User.username: Tweet.content})\n                 .from_(Tweet)\n                 .where(Tweet.content == 'tx'))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"username\" = \"t1\".\"content\" '\n            'FROM \"tweet\" AS \"t1\" WHERE (\"t1\".\"content\" = ?)'), ['tx'])\n\n    def test_update_from_qualnames(self):\n        data = [(1, 'u1-x'), (2, 'u2-x')]\n        vl = ValuesList(data, columns=('id', 'username'), alias='tmp')\n        query = (User\n                 .update({User.username: vl.c.username})\n                 .from_(vl)\n                 .where(User.id == vl.c.id))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"username\" = \"tmp\".\"username\" '\n            'FROM (VALUES (?, ?), (?, ?)) AS \"tmp\"(\"id\", \"username\") '\n            'WHERE (\"users\".\"id\" = \"tmp\".\"id\")'), [1, 'u1-x', 2, 'u2-x'])\n\n    def test_update_from_subselect(self):\n        data = [(1, 'u1-x'), (2, 'u2-x')]\n        vl = ValuesList(data, columns=('id', 'username'), alias='tmp')\n        subq = vl.select(vl.c.id, vl.c.username)\n        query = (User\n                 .update({User.username: subq.c.username})\n                 .from_(subq)\n                 .where(User.id == subq.c.id))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"username\" = \"t1\".\"username\" FROM ('\n            'SELECT \"tmp\".\"id\", \"tmp\".\"username\" '\n            'FROM (VALUES (?, ?), (?, ?)) AS \"tmp\"(\"id\", \"username\")) AS \"t1\" '\n            'WHERE (\"users\".\"id\" = \"t1\".\"id\")'), [1, 'u1-x', 2, 'u2-x'])\n\n    def test_delete(self):\n        query = (Note\n                 .delete()\n                 .where(Note.author << (Person.select(Person.id)\n                                        .where(Person.last == 'cat'))))\n        self.assertSQL(query, ('DELETE FROM \"note\" '\n                               'WHERE (\"note\".\"author_id\" IN ('\n                               'SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" '\n                               'WHERE (\"t1\".\"last\" = ?)))'), ['cat'])\n\n        query = Note.delete().where(Note.author == Person(id=123))\n        self.assertSQL(query, (\n            'DELETE FROM \"note\" WHERE (\"note\".\"author_id\" = ?)'), [123])\n\n    def test_delete_recursive(self):\n        class User(TestModel):\n            username = CharField()\n        class Tweet(TestModel):\n            user = ForeignKeyField(User, backref='tweets')\n            content = TextField()\n        class Relationship(TestModel):\n            from_user = ForeignKeyField(User, backref='relationships')\n            to_user = ForeignKeyField(User, backref='related_to')\n        class Like(TestModel):\n            user = ForeignKeyField(User)\n            tweet = ForeignKeyField(Tweet)\n\n        queries = list(User(id=1).dependencies())\n        accum = []\n        for expr, fk in list(queries):\n            query = fk.model.delete().where(expr)\n            accum.append(__sql__(query))\n\n        self.assertEqual(sorted(accum), [\n            ('DELETE FROM \"like\" WHERE ('\n             '\"like\".\"tweet_id\" IN ('\n             'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" WHERE ('\n             '\"t1\".\"user_id\" = ?)))', [1]),\n            ('DELETE FROM \"like\" WHERE (\"like\".\"user_id\" = ?)', [1]),\n            ('DELETE FROM \"relationship\" '\n             'WHERE (\"relationship\".\"from_user_id\" = ?)', [1]),\n            ('DELETE FROM \"relationship\" '\n             'WHERE (\"relationship\".\"to_user_id\" = ?)', [1]),\n            ('DELETE FROM \"tweet\" WHERE (\"tweet\".\"user_id\" = ?)', [1]),\n        ])\n\n    def test_aliases(self):\n        class A(TestModel):\n            a = CharField()\n        class B(TestModel):\n            b = CharField()\n            a_link = ForeignKeyField(A)\n        class C(TestModel):\n            c = CharField()\n            b_link = ForeignKeyField(B)\n        class D(TestModel):\n            d = CharField()\n            c_link = ForeignKeyField(C)\n\n        query = (D\n                 .select(D.d, C.c)\n                 .join(C)\n                 .where(C.b_link << (\n                     B.select(B.id).join(A).where(A.a == 'a'))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"d\", \"t2\".\"c\" '\n            'FROM \"d\" AS \"t1\" '\n            'INNER JOIN \"c\" AS \"t2\" ON (\"t1\".\"c_link_id\" = \"t2\".\"id\") '\n            'WHERE (\"t2\".\"b_link_id\" IN ('\n            'SELECT \"t3\".\"id\" FROM \"b\" AS \"t3\" '\n            'INNER JOIN \"a\" AS \"t4\" ON (\"t3\".\"a_link_id\" = \"t4\".\"id\") '\n            'WHERE (\"t4\".\"a\" = ?)))'), ['a'])\n\n    def test_schema(self):\n        class WithSchema(TestModel):\n            data = CharField(primary_key=True)\n            class Meta:\n                schema = 'huey'\n\n        query = WithSchema.select().where(WithSchema.data == 'zaizee')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"data\" '\n            'FROM \"huey\".\"with_schema\" AS \"t1\" '\n            'WHERE (\"t1\".\"data\" = ?)'), ['zaizee'])\n\n\n@requires_pglike\nclass TestOnConflictSQL(ModelDatabaseTestCase):\n    requires = [Emp, OCTest, UKVP]\n\n    def test_atomic_update(self):\n        query = OCTest.insert(a='foo', b=1).on_conflict(\n            conflict_target=(OCTest.a,),\n            update={OCTest.b: OCTest.b + 2})\n\n        self.assertSQL(query, (\n            'INSERT INTO \"oc_test\" (\"a\", \"b\", \"c\") VALUES (?, ?, ?) '\n            'ON CONFLICT (\"a\") '\n            'DO UPDATE SET \"b\" = (\"oc_test\".\"b\" + ?) '\n            'RETURNING \"oc_test\".\"id\"'), ['foo', 1, 0, 2])\n\n    def test_on_conflict_do_nothing(self):\n        query = OCTest.insert(a='foo', b=1).on_conflict(action='IGNORE')\n        self.assertSQL(query, (\n            'INSERT INTO \"oc_test\" (\"a\", \"b\", \"c\") VALUES (?, ?, ?) '\n            'ON CONFLICT DO NOTHING '\n            'RETURNING \"oc_test\".\"id\"'), ['foo', 1, 0])\n\n        query = OCTest.insert(a='foo', b=1).on_conflict(\n            conflict_target=(OCTest.a,),\n            action='IGNORE')\n        self.assertSQL(query, (\n            'INSERT INTO \"oc_test\" (\"a\", \"b\", \"c\") VALUES (?, ?, ?) '\n            'ON CONFLICT (\"a\") DO NOTHING '\n            'RETURNING \"oc_test\".\"id\"'), ['foo', 1, 0])\n\n    def test_update_where_clause(self):\n        # Add a new row with the given \"a\" value. If a conflict occurs,\n        # re-insert with b=b+2 so long as the original b < 3.\n        query = OCTest.insert(a='foo', b=1).on_conflict(\n            conflict_target=(OCTest.a,),\n            update={OCTest.b: OCTest.b + 2},\n            where=(OCTest.b < 3))\n        self.assertSQL(query, (\n            'INSERT INTO \"oc_test\" (\"a\", \"b\", \"c\") VALUES (?, ?, ?) '\n            'ON CONFLICT (\"a\") DO UPDATE SET \"b\" = (\"oc_test\".\"b\" + ?) '\n            'WHERE (\"oc_test\".\"b\" < ?) '\n            'RETURNING \"oc_test\".\"id\"'), ['foo', 1, 0, 2, 3])\n\n    def test_conflict_target_constraint_where(self):\n        fields = [UKVP.key, UKVP.value, UKVP.extra]\n        data = [('k1', 1, 2), ('k2', 2, 3)]\n\n        query = (UKVP.insert_many(data, fields)\n                 .on_conflict(conflict_target=(UKVP.key, UKVP.value),\n                              conflict_where=(UKVP.extra > 1),\n                              preserve=(UKVP.extra,),\n                              where=(UKVP.key != 'kx')))\n        self.assertSQL(query, (\n            'INSERT INTO \"ukvp\" (\"key\", \"value\", \"extra\") '\n            'VALUES (?, ?, ?), (?, ?, ?) '\n            'ON CONFLICT (\"key\", \"value\") WHERE (\"extra\" > ?) '\n            'DO UPDATE SET \"extra\" = EXCLUDED.\"extra\" '\n            'WHERE (\"ukvp\".\"key\" != ?) RETURNING \"ukvp\".\"id\"'),\n            ['k1', 1, 2, 'k2', 2, 3, 1, 'kx'])\n\n    def test_preserve_and_update(self):\n        query = (UKVP\n                 .insert(key='k1', value=1, extra=10)\n                 .on_conflict(\n                     conflict_target=(UKVP.key,),\n                     preserve=(UKVP.value,),\n                     update={UKVP.extra: UKVP.extra + 1}))\n        self.assertSQL(query, (\n            'INSERT INTO \"ukvp\" (\"key\", \"value\", \"extra\") '\n            'VALUES (?, ?, ?) '\n            'ON CONFLICT (\"key\") DO UPDATE SET '\n            '\"value\" = EXCLUDED.\"value\", '\n            '\"extra\" = (\"ukvp\".\"extra\" + ?) '\n            'RETURNING \"ukvp\".\"id\"'), ['k1', 1, 10, 1])\n\n    def test_preserve_with_where(self):\n        query = (UKVP\n                 .insert(key='k1', value=1, extra=10)\n                 .on_conflict(\n                     conflict_target=(UKVP.key,),\n                     preserve=(UKVP.value,),\n                     where=(UKVP.extra < 100)))\n        self.assertSQL(query, (\n            'INSERT INTO \"ukvp\" (\"key\", \"value\", \"extra\") '\n            'VALUES (?, ?, ?) '\n            'ON CONFLICT (\"key\") DO UPDATE SET '\n            '\"value\" = EXCLUDED.\"value\" '\n            'WHERE (\"ukvp\".\"extra\" < ?) '\n            'RETURNING \"ukvp\".\"id\"'), ['k1', 1, 10, 100])\n\n    @skip_if(IS_CRDB, 'crdb lol')\n    def test_on_conflict_named_constraint(self):\n        query = (UKVP\n                 .insert(key='k1', value=1)\n                 .on_conflict(\n                     conflict_constraint='ukvp_key',\n                     update={UKVP.value: UKVP.value + 1}))\n        self.assertSQL(query, (\n            'INSERT INTO \"ukvp\" (\"key\", \"value\") VALUES (?, ?) '\n            'ON CONFLICT ON CONSTRAINT \"ukvp_key\" '\n            'DO UPDATE SET \"value\" = (\"ukvp\".\"value\" + ?) '\n            'RETURNING \"ukvp\".\"id\"'), ['k1', 1, 1])\n\n\nclass TestStringsForFieldsa(ModelDatabaseTestCase):\n    database = get_in_memory_db()\n    requires = [Note, Person, Relationship]\n\n    def test_insert(self):\n        qkwargs = Person.insert(first='huey', last='kitty')\n        qliteral = Person.insert({'first': 'huey', 'last': 'kitty'})\n        for query in (qkwargs, qliteral):\n            self.assertSQL(query, (\n                'INSERT INTO \"person\" (\"first\", \"last\") VALUES (?, ?)'),\n                ['huey', 'kitty'])\n\n    def test_insert_many(self):\n        data = [\n            {'first': 'huey', 'last': 'cat'},\n            {'first': 'zaizee', 'last': 'cat'},\n            {'first': 'mickey', 'last': 'dog'}]\n        query = Person.insert_many(data)\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"first\", \"last\") VALUES (?, ?), (?, ?), '\n            '(?, ?)'), ['huey', 'cat', 'zaizee', 'cat', 'mickey', 'dog'])\n\n    def test_update(self):\n        qkwargs = Person.update(last='kitty').where(Person.last == 'cat')\n        qliteral = Person.update({'last': 'kitty'}).where(Person.last == 'cat')\n        for query in (qkwargs, qliteral):\n            self.assertSQL(query, (\n                'UPDATE \"person\" SET \"last\" = ? WHERE (\"person\".\"last\" = ?)'),\n                ['kitty', 'cat'])\n\n\ncompound_db = get_in_memory_db()\n\nclass CompoundTestModel(Model):\n    class Meta:\n        database = compound_db\n\nclass Alpha(CompoundTestModel):\n    alpha = IntegerField()\n\nclass Beta(CompoundTestModel):\n    beta = IntegerField()\n    other = IntegerField(default=0)\n\nclass Gamma(CompoundTestModel):\n    gamma = IntegerField()\n    other = IntegerField(default=1)\n\n\nclass TestModelCompoundSelect(BaseTestCase):\n    def test_unions(self):\n        lhs = Alpha.select(Alpha.alpha)\n        rhs = Beta.select(Beta.beta)\n        self.assertSQL((lhs | rhs), (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" UNION '\n            'SELECT \"t2\".\"beta\" FROM \"beta\" AS \"t2\"'), [])\n\n        rrhs = Gamma.select(Gamma.gamma)\n        query = (lhs | (rhs | rrhs))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" UNION '\n            'SELECT \"t2\".\"beta\" FROM \"beta\" AS \"t2\" UNION '\n            'SELECT \"t3\".\"gamma\" FROM \"gamma\" AS \"t3\"'), [])\n\n    def test_union_same_model(self):\n        q1 = Alpha.select(Alpha.alpha)\n        q2 = Alpha.select(Alpha.alpha)\n        q3 = Alpha.select(Alpha.alpha)\n        compound = (q1 | q2) | q3\n        self.assertSQL(compound, (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" UNION '\n            'SELECT \"t2\".\"alpha\" FROM \"alpha\" AS \"t2\" UNION '\n            'SELECT \"t3\".\"alpha\" FROM \"alpha\" AS \"t3\"'), [])\n\n        compound = q1 | (q2 | q3)\n        self.assertSQL(compound, (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" UNION '\n            'SELECT \"t2\".\"alpha\" FROM \"alpha\" AS \"t2\" UNION '\n            'SELECT \"t3\".\"alpha\" FROM \"alpha\" AS \"t3\"'), [])\n\n    def test_where(self):\n        q1 = Alpha.select(Alpha.alpha).where(Alpha.alpha < 2)\n        q2 = Alpha.select(Alpha.alpha).where(Alpha.alpha > 5)\n        compound = q1 | q2\n        self.assertSQL(compound, (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'WHERE (\"t1\".\"alpha\" < ?) '\n            'UNION '\n            'SELECT \"t2\".\"alpha\" FROM \"alpha\" AS \"t2\" '\n            'WHERE (\"t2\".\"alpha\" > ?)'), [2, 5])\n\n        q3 = Beta.select(Beta.beta).where(Beta.beta < 3)\n        q4 = Beta.select(Beta.beta).where(Beta.beta > 4)\n        compound = q1 | q3\n        self.assertSQL(compound, (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'WHERE (\"t1\".\"alpha\" < ?) '\n            'UNION '\n            'SELECT \"t2\".\"beta\" FROM \"beta\" AS \"t2\" '\n            'WHERE (\"t2\".\"beta\" < ?)'), [2, 3])\n\n        compound = q1 | q3 | q2 | q4\n        self.assertSQL(compound, (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'WHERE (\"t1\".\"alpha\" < ?) '\n            'UNION '\n            'SELECT \"t2\".\"beta\" FROM \"beta\" AS \"t2\" '\n            'WHERE (\"t2\".\"beta\" < ?) '\n            'UNION '\n            'SELECT \"t3\".\"alpha\" FROM \"alpha\" AS \"t3\" '\n            'WHERE (\"t3\".\"alpha\" > ?) '\n            'UNION '\n            'SELECT \"t4\".\"beta\" FROM \"beta\" AS \"t4\" '\n            'WHERE (\"t4\".\"beta\" > ?)'), [2, 3, 5, 4])\n\n    def test_limit(self):\n        lhs = Alpha.select(Alpha.alpha).order_by(Alpha.alpha).limit(3)\n        rhs = Beta.select(Beta.beta).order_by(Beta.beta).limit(4)\n        compound = (lhs | rhs).limit(5)\n        # This may be invalid SQL, but this at least documents the behavior.\n        self.assertSQL(compound, (\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'ORDER BY \"t1\".\"alpha\" LIMIT ? UNION '\n            'SELECT \"t2\".\"beta\" FROM \"beta\" AS \"t2\" '\n            'ORDER BY \"t2\".\"beta\" LIMIT ? LIMIT ?'), [3, 4, 5])\n\n    def test_union_from(self):\n        lhs = Alpha.select(Alpha.alpha).where(Alpha.alpha < 2)\n        rhs = Alpha.select(Alpha.alpha).where(Alpha.alpha > 5)\n        compound = (lhs | rhs).alias('cq')\n        query = Alpha.select(compound.c.alpha).from_(compound)\n        self.assertSQL(query, (\n            'SELECT \"cq\".\"alpha\" FROM ('\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'WHERE (\"t1\".\"alpha\" < ?) '\n            'UNION '\n            'SELECT \"t2\".\"alpha\" FROM \"alpha\" AS \"t2\" '\n            'WHERE (\"t2\".\"alpha\" > ?)) AS \"cq\"'), [2, 5])\n\n        b = Beta.select(Beta.beta).where(Beta.beta < 3)\n        g = Gamma.select(Gamma.gamma).where(Gamma.gamma < 0)\n        compound = (lhs | b | g).alias('cq')\n        query = Alpha.select(SQL('1')).from_(compound)\n        self.assertSQL(query, (\n            'SELECT 1 FROM ('\n            'SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'WHERE (\"t1\".\"alpha\" < ?) '\n            'UNION SELECT \"t2\".\"beta\" FROM \"beta\" AS \"t2\" '\n            'WHERE (\"t2\".\"beta\" < ?) '\n            'UNION SELECT \"t3\".\"gamma\" FROM \"gamma\" AS \"t3\" '\n            'WHERE (\"t3\".\"gamma\" < ?)) AS \"cq\"'), [2, 3, 0])\n\n    def test_parentheses(self):\n        query = (Alpha.select().where(Alpha.alpha < 2) |\n                 Beta.select(Beta.id, Beta.beta).where(Beta.beta > 3))\n        self.assertSQL(query, (\n            '(SELECT \"t1\".\"id\", \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'WHERE (\"t1\".\"alpha\" < ?)) '\n            'UNION '\n            '(SELECT \"t2\".\"id\", \"t2\".\"beta\" FROM \"beta\" AS \"t2\" '\n            'WHERE (\"t2\".\"beta\" > ?))'),\n            [2, 3], compound_select_parentheses=True)\n\n    def test_where_in(self):\n        union = (Alpha.select(Alpha.alpha) |\n                 Beta.select(Beta.beta))\n        query = Alpha.select().where(Alpha.alpha << union)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"alpha\" '\n            'FROM \"alpha\" AS \"t1\" '\n            'WHERE (\"t1\".\"alpha\" IN '\n            '(SELECT \"t1\".\"alpha\" FROM \"alpha\" AS \"t1\" '\n            'UNION '\n            'SELECT \"t2\".\"beta\" FROM \"beta\" AS \"t2\"))'), [])\n\n\nclass TestModelIndex(BaseTestCase):\n    database = SqliteDatabase(None)\n\n    def test_model_index(self):\n        class Article(Model):\n            name = TextField()\n            timestamp = TimestampField()\n            status = IntegerField()\n            flags = IntegerField()\n\n        aidx = ModelIndex(Article, (Article.name, Article.timestamp),)\n        self.assertSQL(aidx, (\n            'CREATE INDEX IF NOT EXISTS \"article_name_timestamp\" ON \"article\" '\n            '(\"name\", \"timestamp\")'), [])\n\n        aidx = aidx.where(Article.status == 1)\n        self.assertSQL(aidx, (\n            'CREATE INDEX IF NOT EXISTS \"article_name_timestamp\" ON \"article\" '\n            '(\"name\", \"timestamp\") '\n            'WHERE (\"status\" = ?)'), [1])\n\n        aidx = ModelIndex(Article, (Article.timestamp.desc(),\n                                    Article.flags.bin_and(4)), unique=True)\n        self.assertSQL(aidx, (\n            'CREATE UNIQUE INDEX IF NOT EXISTS \"article_timestamp\" '\n            'ON \"article\" (\"timestamp\" DESC, (\"flags\" & ?))'), [4])\n\n    def test_unique_index_nulls(self):\n        class A(Model):\n            a = CharField()\n            b = CharField()\n            class Meta:\n                database = self.database\n\n        idx = ModelIndex(A, ('a', 'b'), unique=True)\n        self.assertSQL(A._schema._create_index(idx), (\n            'CREATE UNIQUE INDEX IF NOT EXISTS '\n            '\"a_a_b\" ON \"a\" (a, b)'))\n\n        idx = idx.nulls_distinct(False)\n        self.assertSQL(A._schema._create_index(idx), (\n            'CREATE UNIQUE INDEX IF NOT EXISTS '\n            '\"a_a_b\" ON \"a\" (a, b) NULLS NOT DISTINCT'))\n\n        idx = idx.nulls_distinct(True)\n        self.assertSQL(A._schema._create_index(idx), (\n            'CREATE UNIQUE INDEX IF NOT EXISTS '\n            '\"a_a_b\" ON \"a\" (a, b) NULLS DISTINCT'))\n\n        idx._unique = False\n        self.assertRaises(ValueError, lambda: idx.nulls_distinct(True))\n        self.assertRaises(ValueError, lambda: idx.nulls_distinct(False))\n\n\nclass TestModelArgument(BaseTestCase):\n    database = SqliteDatabase(None)\n\n    def test_model_as_argument(self):\n        class Post(TestModel):\n            content = TextField()\n            timestamp = DateTimeField()\n            class Meta:\n                database = self.database\n\n        query = (Post\n                 .select(Post.id, fn.score(Post).alias('score'))\n                 .order_by(Post.timestamp))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", score(\"t1\") AS \"score\" '\n            'FROM \"post\" AS \"t1\" ORDER BY \"t1\".\"timestamp\"'), [])\n"
  },
  {
    "path": "tests/models.py",
    "content": "import datetime\nimport threading\nimport time\nimport unittest\nfrom unittest import mock\n\nfrom peewee import *\nfrom peewee import Entity\nfrom peewee import NodeList\nfrom peewee import SubclassAwareMetadata\nfrom peewee import sort_models\n\nfrom .base import db\nfrom .base import get_in_memory_db\nfrom .base import new_connection\nfrom .base import requires_models\nfrom .base import requires_mysql\nfrom .base import requires_pglike\nfrom .base import requires_postgresql\nfrom .base import requires_sqlite\nfrom .base import skip_if\nfrom .base import skip_unless\nfrom .base import BaseTestCase\nfrom .base import IS_CRDB\nfrom .base import IS_MYSQL\nfrom .base import IS_MYSQL_ADVANCED_FEATURES\nfrom .base import IS_POSTGRESQL\nfrom .base import IS_SQLITE\nfrom .base import IS_SQLITE_OLD\nfrom .base import IS_SQLITE_15  # Row-values.\nfrom .base import IS_SQLITE_24  # Upsert.\nfrom .base import IS_SQLITE_25  # Window functions.\nfrom .base import IS_SQLITE_30  # FILTER clause functions.\nfrom .base import IS_SQLITE_9\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base_models import *\n\n\nclass Color(TestModel):\n    name = CharField(primary_key=True)\n    is_neutral = BooleanField(default=False)\n\n\nclass Post(TestModel):\n    content = TextField(column_name='Content')\n    timestamp = DateTimeField(column_name='TimeStamp',\n                              default=datetime.datetime.now)\n\n\nclass PostNote(TestModel):\n    post = ForeignKeyField(Post, backref='notes', primary_key=True)\n    note = TextField()\n\n\nclass Point(TestModel):\n    x = IntegerField()\n    y = IntegerField()\n    class Meta:\n        primary_key = False\n\n\nclass CPK(TestModel):\n    key = CharField()\n    value = IntegerField()\n    extra = IntegerField()\n    class Meta:\n        primary_key = CompositeKey('key', 'value')\n\n\nclass City(TestModel):\n    name = CharField()\n\nclass Venue(TestModel):\n    name = CharField()\n    city = ForeignKeyField(City, backref='venues')\n    city_n = ForeignKeyField(City, backref='venues_n', null=True)\n\nclass Event(TestModel):\n    name = CharField()\n    venue = ForeignKeyField(Venue, backref='events', null=True)\n\n\nclass TestModelAPIs(ModelTestCase):\n    def add_user(self, username):\n        return User.create(username=username)\n\n    def add_tweets(self, user, *tweets):\n        accum = []\n        for tweet in tweets:\n            accum.append(Tweet.create(user=user, content=tweet))\n        return accum\n\n    @requires_models(Point)\n    def test_no_primary_key(self):\n        p11 = Point.create(x=1, y=1)\n        p33 = Point.create(x=3, y=3)\n\n        p_db = Point.get((Point.x == 3) & (Point.y == 3))\n        self.assertEqual(p_db.x, 3)\n        self.assertEqual(p_db.y, 3)\n\n    @requires_models(Post, PostNote)\n    def test_pk_is_fk(self):\n        with self.database.atomic():\n            p1 = Post.create(content='p1')\n            p2 = Post.create(content='p2')\n            p1n = PostNote.create(post=p1, note='p1n')\n            p2n = PostNote.create(post=p2, note='p2n')\n\n        with self.assertQueryCount(2):\n            pn = PostNote.get(PostNote.note == 'p1n')\n            self.assertEqual(pn.post.content, 'p1')\n\n        with self.assertQueryCount(1):\n            pn = (PostNote\n                  .select(PostNote, Post)\n                  .join(Post)\n                  .where(PostNote.note == 'p2n')\n                  .get())\n            self.assertEqual(pn.post.content, 'p2')\n\n        if not IS_SQLITE:\n            exc_class = (ProgrammingError, IntegrityError)\n            with self.database.atomic() as txn:\n                self.assertRaises(exc_class, PostNote.create, note='pxn')\n                txn.rollback()\n\n    @requires_models(User, Tweet)\n    def test_assertQueryCount(self):\n        self.add_tweets(self.add_user('charlie'), 'foo', 'bar', 'baz')\n        def do_test(n):\n            with self.assertQueryCount(n):\n                authors = [tweet.user.username for tweet in Tweet.select()]\n\n        self.assertRaises(AssertionError, do_test, 1)\n        self.assertRaises(AssertionError, do_test, 3)\n        do_test(4)\n        self.assertRaises(AssertionError, do_test, 5)\n\n    @requires_models(Post)\n    def test_column_field_translation(self):\n        ts = datetime.datetime(2017, 2, 1, 13, 37)\n        ts2 = datetime.datetime(2017, 2, 2, 13, 37)\n        p = Post.create(content='p1', timestamp=ts)\n        p2 = Post.create(content='p2', timestamp=ts2)\n\n        p_db = Post.get(Post.content == 'p1')\n        self.assertEqual(p_db.content, 'p1')\n        self.assertEqual(p_db.timestamp, ts)\n\n        pd1, pd2 = Post.select().order_by(Post.id).dicts()\n        self.assertEqual(pd1['content'], 'p1')\n        self.assertEqual(pd1['timestamp'], ts)\n        self.assertEqual(pd2['content'], 'p2')\n        self.assertEqual(pd2['timestamp'], ts2)\n\n    @requires_models(User)\n    def test_insert_many(self):\n        data = [('u%02d' % i,) for i in range(100)]\n        with self.database.atomic():\n            for chunk in chunked(data, 10):\n                User.insert_many(chunk).execute()\n\n        self.assertEqual(User.select().count(), 100)\n        names = [u.username for u in User.select().order_by(User.username)]\n        self.assertEqual(names, ['u%02d' % i for i in range(100)])\n\n    @requires_models(DfltM)\n    def test_insert_many_defaults_nullable(self):\n        data = [\n            {'name': 'd1'},\n            {'name': 'd2', 'dflt1': 10},\n            {'name': 'd3', 'dflt2': 30},\n            {'name': 'd4', 'dfltn': 40}]\n        fields = [DfltM.name, DfltM.dflt1, DfltM.dflt2, DfltM.dfltn]\n        DfltM.insert_many(data, fields).execute()\n\n        expected = [\n            ('d1', 1, 2, None),\n            ('d2', 10, 2, None),\n            ('d3', 1, 30, None),\n            ('d4', 1, 2, 40)]\n        query = DfltM.select().order_by(DfltM.name)\n        actual = [(d.name, d.dflt1, d.dflt2, d.dfltn) for d in query]\n        self.assertEqual(actual, expected)\n\n    @requires_models(User, Tweet)\n    def test_create(self):\n        with self.assertQueryCount(1):\n            huey = self.add_user('huey')\n            self.assertEqual(huey.username, 'huey')\n            self.assertTrue(isinstance(huey.id, int))\n            self.assertTrue(huey.id > 0)\n\n        with self.assertQueryCount(1):\n            tweet = Tweet.create(user=huey, content='meow')\n            self.assertEqual(tweet.user.id, huey.id)\n            self.assertEqual(tweet.user.username, 'huey')\n            self.assertEqual(tweet.content, 'meow')\n            self.assertTrue(isinstance(tweet.id, int))\n            self.assertTrue(tweet.id > 0)\n\n    @requires_models(User)\n    def test_bulk_create(self):\n        users = [User(username='u%s' % i) for i in range(5)]\n        self.assertEqual(User.select().count(), 0)\n\n        with self.assertQueryCount(1):\n            User.bulk_create(users)\n\n        self.assertEqual(User.select().count(), 5)\n        self.assertEqual([u.username for u in User.select().order_by(User.id)],\n                         ['u0', 'u1', 'u2', 'u3', 'u4'])\n\n        if IS_POSTGRESQL:\n            self.assertEqual([u.id for u in User.select().order_by(User.id)],\n                             [user.id for user in users])\n\n    @requires_models(User)\n    def test_bulk_create_empty(self):\n        self.assertEqual(User.select().count(), 0)\n        User.bulk_create([])\n\n    @requires_models(User)\n    def test_bulk_create_batching(self):\n        users = [User(username=str(i)) for i in range(10)]\n        with self.assertQueryCount(4):\n            User.bulk_create(users, 3)\n\n        self.assertEqual(User.select().count(), 10)\n        self.assertEqual([u.username for u in User.select().order_by(User.id)],\n                         list('0123456789'))\n\n        if IS_POSTGRESQL:\n            self.assertEqual([u.id for u in User.select().order_by(User.id)],\n                             [user.id for user in users])\n\n    @requires_models(Person)\n    def test_bulk_create_error(self):\n        people = [Person(first='a', last='b'),\n                  Person(first='b', last='c'),\n                  Person(first='a', last='b')]\n        with self.assertRaises(IntegrityError):\n            with self.database.atomic():\n                Person.bulk_create(people)\n        self.assertEqual(Person.select().count(), 0)\n\n    @requires_models(CPK)\n    def test_bulk_create_composite_key(self):\n        self.assertEqual(CPK.select().count(), 0)\n        items = [CPK(key='k1', value=1, extra=1),\n                 CPK(key='k2', value=2, extra=2)]\n        CPK.bulk_create(items)\n        self.assertEqual([(c.key, c.value, c.extra) for c in items],\n                         [('k1', 1, 1), ('k2', 2, 2)])\n\n        query = CPK.select().order_by(CPK.key).tuples()\n        self.assertEqual(list(query), [('k1', 1, 1), ('k2', 2, 2)])\n\n    @requires_models(Person)\n    def test_bulk_update(self):\n        data = [('f%s' % i, 'l%s' % i, datetime.date(1980, i, i))\n                for i in range(1, 5)]\n        Person.insert_many(data).execute()\n\n        p1, p2, p3, p4 = list(Person.select().order_by(Person.id))\n        p1.first = 'f1-x'\n        p1.last = 'l1-x'\n        p2.first = 'f2-y'\n        p3.last = 'l3-z'\n\n        with self.assertQueryCount(1):\n            n = Person.bulk_update([p1, p2, p3, p4], ['first', 'last'])\n            self.assertEqual(n, 3 if IS_MYSQL else 4)\n\n        query = Person.select().order_by(Person.id)\n        self.assertEqual([(p.first, p.last) for p in query], [\n            ('f1-x', 'l1-x'),\n            ('f2-y', 'l2'),\n            ('f3', 'l3-z'),\n            ('f4', 'l4')])\n\n        # Modify multiple fields, but only update \"first\".\n        p1.first = 'f1-x2'\n        p1.last = 'l1-x2'\n        p2.first = 'f2-y2'\n        p3.last = 'f3-z2'\n        with self.assertQueryCount(2):  # Two batches, so two queries.\n            n = Person.bulk_update([p1, p2, p3, p4], [Person.first], 2)\n            self.assertEqual(n, 2 if IS_MYSQL else 4)\n\n        query = Person.select().order_by(Person.id)\n        self.assertEqual([(p.first, p.last) for p in query], [\n            ('f1-x2', 'l1-x'),\n            ('f2-y2', 'l2'),\n            ('f3', 'l3-z'),\n            ('f4', 'l4')])\n\n    @requires_models(User, Tweet)\n    def test_bulk_update_foreign_key(self):\n        for username in ('charlie', 'huey', 'zaizee'):\n            user = User.create(username=username)\n            for i in range(2):\n                Tweet.create(user=user, content='%s-%s' % (username, i))\n\n        c, h, z = list(User.select().order_by(User.id))\n        c0, c1, h0, h1, z0, z1 = list(Tweet.select().order_by(Tweet.id))\n        c0.content = 'charlie-0x'\n        c1.user = h\n        h0.user = z\n        h1.content = 'huey-1x'\n        z0.user = c\n        z0.content = 'zaizee-0x'\n\n        with self.assertQueryCount(1):\n            Tweet.bulk_update([c0, c1, h0, h1, z0, z1], ['user', 'content'])\n\n        query = (Tweet\n                 .select(Tweet.content, User.username)\n                 .join(User)\n                 .order_by(Tweet.id)\n                 .objects())\n        self.assertEqual([(t.username, t.content) for t in query], [\n            ('charlie', 'charlie-0x'),\n            ('huey', 'charlie-1'),\n            ('zaizee', 'huey-0'),\n            ('huey', 'huey-1x'),\n            ('charlie', 'zaizee-0x'),\n            ('zaizee', 'zaizee-1')])\n\n    @requires_models(Person)\n    def test_bulk_update_integrityerror(self):\n        people = [Person(first='f%s' % i, last='l%s' % i, dob='1980-01-01')\n                  for i in range(10)]\n        Person.bulk_create(people)\n\n        # Get list of people w/the IDs populated. They will not be set if the\n        # underlying DB is Sqlite or MySQL.\n        people = list(Person.select().order_by(Person.id))\n\n        # First we'll just modify all the first and last names.\n        for person in people:\n            person.first += '-x'\n            person.last += '-x'\n\n        # Now we'll introduce an issue that will cause an integrity error.\n        p3, p7 = people[3], people[7]\n        p3.first = p7.first = 'fx'\n        p3.last = p7.last = 'lx'\n        with self.assertRaises(IntegrityError):\n            with self.assertQueryCount(1):\n                with self.database.atomic():\n                    Person.bulk_update(people, fields=['first', 'last'])\n\n        with self.assertRaises(IntegrityError):\n            # 10 objects, batch size=4, so 0-3, 4-7, 8&9. But we never get to 8\n            # and 9 because of the integrity error processing the 2nd batch.\n            with self.assertQueryCount(2):\n                with self.database.atomic():\n                    Person.bulk_update(people, ['first', 'last'], 4)\n\n        # Ensure no changes were made.\n        vals = [(p.first, p.last) for p in Person.select().order_by(Person.id)]\n        self.assertEqual(vals, [('f%s' % i, 'l%s' % i) for i in range(10)])\n\n    @requires_models(User, Tweet)\n    def test_bulk_update_apply_dbvalue(self):\n        u = User.create(username='u')\n        t1, t2, t3 = [Tweet.create(user=u, content=str(i)) for i in (1, 2, 3)]\n\n        # If we don't end up applying the field's db_value() to these timestamp\n        # values, then we will end up with bad data or an error when attempting\n        # to do the update.\n        t1.timestamp = datetime.datetime(2019, 1, 2, 3, 4, 5)\n        t2.timestamp = datetime.date(2019, 1, 3)\n        t3.timestamp = 1337133700  # 2012-05-15T21:1:40.\n        t3_dt = datetime.datetime.fromtimestamp(1337133700)\n        Tweet.bulk_update([t1, t2, t3], fields=['timestamp'])\n\n        # Ensure that the values were handled appropriately.\n        t1, t2, t3 = list(Tweet.select().order_by(Tweet.id))\n        self.assertEqual(t1.timestamp, datetime.datetime(2019, 1, 2, 3, 4, 5))\n        self.assertEqual(t2.timestamp, datetime.datetime(2019, 1, 3, 0, 0, 0))\n        self.assertEqual(t3.timestamp, t3_dt)\n\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL or IS_CRDB)\n    @requires_models(CPK)\n    def test_bulk_update_cte(self):\n        CPK.insert_many([('k1', 1, 1), ('k2', 2, 2), ('k3', 3, 3)]).execute()\n\n        # We can also do a bulk-update using ValuesList when the primary-key of\n        # the model is a composite-pk.\n        new_values = [('k1', 1, 10), ('k3', 3, 30)]\n        cte = ValuesList(new_values).cte('new_values', columns=('k', 'v', 'x'))\n\n        # We have to use a subquery to update the individual column, as SQLite\n        # does not support UPDATE/FROM syntax.\n        subq = (cte\n                .select(cte.c.x)\n                .where(CPK._meta.primary_key == (cte.c.k, cte.c.v)))\n\n        # Perform the update, assigning extra the new value from the values\n        # list, and restricting the overall update using the composite pk.\n        res = (CPK\n               .update(extra=subq)\n               .where(CPK._meta.primary_key.in_(cte.select(cte.c.k, cte.c.v)))\n               .with_cte(cte)\n               .execute())\n\n        self.assertEqual(list(sorted(CPK.select().tuples())), [\n            ('k1', 1, 10), ('k2', 2, 2), ('k3', 3, 30)])\n\n    @requires_models(User)\n    def test_insert_rowcount(self):\n        User.create(username='u0')  # Ensure that last insert ID != rowcount.\n\n        iq = User.insert_many([(u,) for u in ('u1', 'u2', 'u3')])\n        self.assertEqual(iq.as_rowcount().execute(), 3)\n\n        # Now explicitly specify empty returning() for all DBs.\n        iq = User.insert_many([(u,) for u in ('u4', 'u5')]).returning()\n        self.assertEqual(iq.as_rowcount().execute(), 2)\n\n        query = (User\n                 .select(User.username.concat('-x'))\n                 .where(User.username.in_(['u1', 'u2'])))\n        iq = User.insert_from(query, ['username'])\n        self.assertEqual(iq.as_rowcount().execute(), 2)\n\n        query = (User\n                 .select(User.username.concat('-y'))\n                 .where(User.username.in_(['u3', 'u4'])))\n        iq = User.insert_from(query, ['username']).returning()\n        self.assertEqual(iq.as_rowcount().execute(), 2)\n\n        query = User.insert({'username': 'u5'})\n        self.assertEqual(query.as_rowcount().execute(), 1)\n\n    @skip_if(IS_POSTGRESQL or IS_CRDB, 'requires sqlite or mysql')\n    @requires_models(Emp)\n    def test_replace_rowcount(self):\n        Emp.create(first='beanie', last='cat', empno='998')\n\n        data = [\n            ('beanie', 'cat', '999'),\n            ('mickey', 'dog', '123')]\n        fields = (Emp.first, Emp.last, Emp.empno)\n\n        # MySQL returns 3, Sqlite 2. However, older stdlib sqlite3 does not\n        # work properly, so we don't assert a result count here.\n        Emp.replace_many(data, fields=fields).execute()\n\n        query = Emp.select(Emp.first, Emp.last, Emp.empno).order_by(Emp.last)\n        self.assertEqual(list(query.tuples()), [\n            ('beanie', 'cat', '999'),\n            ('mickey', 'dog', '123')])\n\n    @requires_models(User, Tweet)\n    def test_get_shortcut(self):\n        huey = self.add_user('huey')\n        self.add_tweets(huey, 'meow', 'purr', 'wheeze')\n        mickey = self.add_user('mickey')\n        self.add_tweets(mickey, 'woof', 'yip')\n\n        # Lookup using just the ID.\n        huey_db = User.get(huey.id)\n        self.assertEqual(huey.id, huey_db.id)\n\n        # Lookup using an expression.\n        huey_db = User.get(User.username == 'huey')\n        self.assertEqual(huey.id, huey_db.id)\n        mickey_db = User.get(User.username == 'mickey')\n        self.assertEqual(mickey.id, mickey_db.id)\n        self.assertEqual(User.get(username='mickey').id, mickey.id)\n\n        # No results is an exception.\n        self.assertRaises(User.DoesNotExist, User.get, User.username == 'x')\n        # Multiple results is OK.\n        tweet = Tweet.get(Tweet.user == huey_db)\n        self.assertTrue(tweet.content in ('meow', 'purr', 'wheeze'))\n\n        # We cannot traverse a join like this.\n        @self.database.atomic()\n        def has_error():\n            Tweet.get(User.username == 'huey')\n        self.assertRaises(Exception, has_error)\n\n        # This is OK, though.\n        tweet = Tweet.get(user__username='mickey')\n        self.assertTrue(tweet.content in ('woof', 'yip'))\n\n        tweet = Tweet.get(content__ilike='w%',\n                          user__username__ilike='%ck%')\n        self.assertEqual(tweet.content, 'woof')\n\n    @requires_models(User)\n    def test_get_with_alias(self):\n        huey = self.add_user('huey')\n        query = (User\n                 .select(User.username.alias('name'))\n                 .where(User.username == 'huey'))\n        obj = query.dicts().get()\n        self.assertEqual(obj, {'name': 'huey'})\n\n        obj = query.objects().get()\n        self.assertEqual(obj.name, 'huey')\n\n    @requires_models(User, Tweet)\n    def test_get_or_none(self):\n        huey = self.add_user('huey')\n        self.assertEqual(User.get_or_none(User.username == 'huey').username,\n                         'huey')\n        self.assertIsNone(User.get_or_none(User.username == 'foo'))\n\n    @requires_models(User, Tweet)\n    def test_model_select_get_or_none(self):\n        huey = self.add_user('huey')\n        huey_db = User.select().where(User.username == 'huey').get_or_none()\n        self.assertEqual(huey_db.username, 'huey')\n        self.assertIsNone(\n            User.select().where(User.username == 'foo').get_or_none())\n\n    @requires_models(User, Color)\n    def test_get_by_id(self):\n        huey = self.add_user('huey')\n        self.assertEqual(User.get_by_id(huey.id).username, 'huey')\n\n        Color.insert_many([\n            {'name': 'red', 'is_neutral': False},\n            {'name': 'blue', 'is_neutral': False}]).execute()\n        self.assertEqual(Color.get_by_id('red').name, 'red')\n        self.assertRaises(Color.DoesNotExist, Color.get_by_id, 'green')\n\n        self.assertEqual(Color['red'].name, 'red')\n        self.assertRaises(Color.DoesNotExist, lambda: Color['green'])\n\n    @requires_models(User, Color)\n    def test_get_set_item(self):\n        huey = self.add_user('huey')\n        huey_db = User[huey.id]\n        self.assertEqual(huey_db.username, 'huey')\n\n        User[huey.id] = {'username': 'huey-x'}\n        huey_db = User[huey.id]\n        self.assertEqual(huey_db.username, 'huey-x')\n        del User[huey.id]\n        self.assertEqual(len(User), 0)\n\n        # Allow creation by specifying None for key.\n        User[None] = {'username': 'zaizee'}\n        User.get(User.username == 'zaizee')\n\n    @requires_models(User)\n    def test_get_or_create(self):\n        huey, created = User.get_or_create(username='huey')\n        self.assertTrue(created)\n        huey2, created2 = User.get_or_create(username='huey')\n        self.assertFalse(created2)\n        self.assertEqual(huey.id, huey2.id)\n\n    @requires_models(Category)\n    def test_get_or_create_self_referential_fk(self):\n        parent = Category.create(name='parent')\n        child, created = Category.get_or_create(parent=parent, name='child')\n        child_db = Category.get(Category.parent == parent)\n        self.assertEqual(child_db.parent.name, 'parent')\n        self.assertEqual(child_db.name, 'child')\n\n    @requires_models(Person)\n    def test_get_or_create_defaults(self):\n        p, created = Person.get_or_create(first='huey', defaults={\n            'last': 'cat',\n            'dob': datetime.date(2010, 7, 1)})\n        self.assertTrue(created)\n\n        p_db = Person.get(Person.first == 'huey')\n        self.assertEqual(p_db.first, 'huey')\n        self.assertEqual(p_db.last, 'cat')\n        self.assertEqual(p_db.dob, datetime.date(2010, 7, 1))\n\n        p2, created = Person.get_or_create(first='huey', defaults={\n            'last': 'kitten',\n            'dob': datetime.date(2020, 1, 1)})\n        self.assertFalse(created)\n        self.assertEqual(p2.first, 'huey')\n        self.assertEqual(p2.last, 'cat')\n        self.assertEqual(p2.dob, datetime.date(2010, 7, 1))\n\n    @requires_models(Person)\n    def test_save(self):\n        huey = Person(first='huey', last='cat', dob=datetime.date(2010, 7, 1))\n        self.assertTrue(huey.save() > 0)\n        self.assertTrue(huey.id is not None)  # Ensure PK is set.\n        orig_id = huey.id\n\n        # Test initial save (INSERT) worked and data is all present.\n        huey_db = Person.get(first='huey', last='cat')\n        self.assertEqual(huey_db.id, huey.id)\n        self.assertEqual(huey_db.first, 'huey')\n        self.assertEqual(huey_db.last, 'cat')\n        self.assertEqual(huey_db.dob, datetime.date(2010, 7, 1))\n\n        # Make a change and do a second save (UPDATE).\n        huey.dob = datetime.date(2010, 7, 2)\n        self.assertTrue(huey.save() > 0)\n        self.assertEqual(huey.id, orig_id)\n\n        # Test UPDATE worked correctly.\n        huey_db = Person.get(first='huey', last='cat')\n        self.assertEqual(huey_db.id, huey.id)\n        self.assertEqual(huey_db.first, 'huey')\n        self.assertEqual(huey_db.last, 'cat')\n        self.assertEqual(huey_db.dob, datetime.date(2010, 7, 2))\n\n        self.assertEqual(Person.select().count(), 1)\n\n    @requires_models(Person)\n    def test_save_only(self):\n        huey = Person(first='huey', last='cat', dob=datetime.date(2010, 7, 1))\n        huey.save()\n\n        huey.first = 'huker'\n        huey.last = 'kitten'\n        self.assertTrue(huey.save(only=('first',)) > 0)\n\n        huey_db = Person.get_by_id(huey.id)\n        self.assertEqual(huey_db.first, 'huker')\n        self.assertEqual(huey_db.last, 'cat')\n        self.assertEqual(huey_db.dob, datetime.date(2010, 7, 1))\n\n        huey.first = 'hubie'\n        self.assertTrue(huey.save(only=[Person.last]) > 0)\n\n        huey_db = Person.get_by_id(huey.id)\n        self.assertEqual(huey_db.first, 'huker')\n        self.assertEqual(huey_db.last, 'kitten')\n        self.assertEqual(huey_db.dob, datetime.date(2010, 7, 1))\n\n        self.assertEqual(Person.select().count(), 1)\n\n    @requires_models(Color, User)\n    def test_save_force(self):\n        huey = User(username='huey')\n        self.assertTrue(huey.save() > 0)\n        huey_id = huey.id\n\n        huey.username = 'zaizee'\n        self.assertTrue(huey.save(force_insert=True, only=('username',)) > 0)\n        zaizee_id = huey.id\n        self.assertTrue(huey_id != zaizee_id)\n\n        query = User.select().order_by(User.username)\n        self.assertEqual([user.username for user in query], ['huey', 'zaizee'])\n\n        color = Color(name='red')\n        self.assertFalse(bool(color.save()))\n        self.assertEqual(Color.select().count(), 0)\n\n        color = Color(name='blue')\n        color.save(force_insert=True)\n        self.assertEqual(Color.select().count(), 1)\n\n        with self.database.atomic():\n            self.assertRaises(IntegrityError,\n                              color.save,\n                              force_insert=True)\n\n    @requires_models(User, Tweet)\n    def test_populate_unsaved_relations(self):\n        user = User(username='charlie')\n        tweet = Tweet(user=user, content='foo')\n\n        self.assertTrue(user.save())\n        self.assertTrue(user.id is not None)\n        with self.assertQueryCount(1):\n            self.assertEqual(tweet.user_id, user.id)\n            self.assertTrue(tweet.save())\n            self.assertEqual(tweet.user_id, user.id)\n\n        tweet_db = Tweet.get(Tweet.content == 'foo')\n        self.assertEqual(tweet_db.user.username, 'charlie')\n\n    @requires_models(User, Tweet)\n    def test_model_select(self):\n        huey = self.add_user('huey')\n        mickey = self.add_user('mickey')\n        zaizee = self.add_user('zaizee')\n\n        self.add_tweets(huey, 'meow', 'hiss', 'purr')\n        self.add_tweets(mickey, 'woof', 'whine')\n\n        with self.assertQueryCount(1):\n            query = (Tweet\n                     .select(Tweet.content, User.username)\n                     .join(User)\n                     .order_by(User.username, Tweet.content))\n            self.assertSQL(query, (\n                'SELECT \"t1\".\"content\", \"t2\".\"username\" '\n                'FROM \"tweet\" AS \"t1\" '\n                'INNER JOIN \"users\" AS \"t2\" '\n                'ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n                'ORDER BY \"t2\".\"username\", \"t1\".\"content\"'), [])\n\n            tweets = list(query)\n            self.assertEqual([(t.content, t.user.username) for t in tweets], [\n                ('hiss', 'huey'),\n                ('meow', 'huey'),\n                ('purr', 'huey'),\n                ('whine', 'mickey'),\n                ('woof', 'mickey')])\n\n    @requires_pglike\n    @requires_models(User, Tweet)\n    def test_distinct_on(self):\n        u1, u2 = self.add_user('u1'), self.add_user('u2')\n        self.add_tweets(u1, 'u1-t1', 'u1-t2', 'u1-t3')\n        self.add_tweets(u2, 'u2-t1')\n\n        query = (Tweet\n                 .select(Tweet.user, Tweet.content)\n                 .join(User)\n                 .distinct(Tweet.user)\n                 .order_by(Tweet.user, Tweet.timestamp))\n        self.assertEqual([(t.user_id, t.content) for t in query],\n                         [(u1.id, 'u1-t1'), (u2.id, 'u2-t1')])\n\n    @requires_models(User, Tweet, Favorite)\n    def test_join_two_fks(self):\n        with self.database.atomic():\n            huey = self.add_user('huey')\n            mickey = self.add_user('mickey')\n            h_m, h_p, h_h = self.add_tweets(huey, 'meow', 'purr', 'hiss')\n            m_w, m_b = self.add_tweets(mickey, 'woof', 'bark')\n            Favorite.create(user=huey, tweet=m_w)\n            Favorite.create(user=mickey, tweet=h_m)\n            Favorite.create(user=mickey, tweet=h_p)\n\n        with self.assertQueryCount(1):\n            UA = User.alias()\n            query = (Favorite\n                     .select(Favorite, Tweet, User, UA)\n                     .join(Tweet)\n                     .join(User)\n                     .switch(Favorite)\n                     .join(UA, on=Favorite.user)\n                     .order_by(Favorite.id))\n\n            accum = [(f.tweet.user.username, f.tweet.content, f.user.username)\n                     for f in query]\n\n        self.assertEqual(accum, [\n            ('mickey', 'woof', 'huey'),\n            ('huey', 'meow', 'mickey'),\n            ('huey', 'purr', 'mickey')])\n\n        with self.assertQueryCount(5):\n            # Test intermediate models not selected.\n            query = (Favorite\n                     .select()\n                     .join(Tweet)\n                     .switch(Favorite)\n                     .join(User)\n                     .where(User.username == 'mickey')\n                     .order_by(Favorite.id))\n\n            accum = [(f.user.username, f.tweet.content) for f in query]\n\n        self.assertEqual(accum, [('mickey', 'meow'), ('mickey', 'purr')])\n\n    @requires_models(A, B, C)\n    def test_join_issue_1482(self):\n        a1 = A.create(a='a1')\n        b1 = B.create(a=a1, b='b1')\n        c1 = C.create(b=b1, c='c1')\n\n        with self.assertQueryCount(3):\n            query = C.select().join(B).join(A).where(A.a == 'a1')\n            accum = [(c.c, c.b.b, c.b.a.a) for c in query]\n\n        self.assertEqual(accum, [('c1', 'b1', 'a1')])\n\n    @requires_models(A, B, C)\n    def test_join_empty_intermediate_model(self):\n        a1 = A.create(a='a1')\n        a2 = A.create(a='a2')\n        b11 = B.create(a=a1, b='b11')\n        b12 = B.create(a=a1, b='b12')\n        b21 = B.create(a=a2, b='b21')\n        c111 = C.create(b=b11, c='c111')\n        c112 = C.create(b=b11, c='c112')\n        c211 = C.create(b=b21, c='c211')\n\n        with self.assertQueryCount(1):\n            query = C.select(C, A.a).join(B).join(A).order_by(C.c)\n            accum = [(c.c, c.b.a.a) for c in query]\n\n        self.assertEqual(accum, [\n            ('c111', 'a1'),\n            ('c112', 'a1'),\n            ('c211', 'a2')])\n\n        with self.assertQueryCount(1):\n            query = C.select(C, B, A).join(B).join(A).order_by(C.c)\n            accum = [(c.c, c.b.b, c.b.a.a) for c in query]\n\n        self.assertEqual(accum, [\n            ('c111', 'b11', 'a1'),\n            ('c112', 'b11', 'a1'),\n            ('c211', 'b21', 'a2')])\n\n    @requires_models(City, Venue, Event)\n    def test_join_empty_relations(self):\n        with self.database.atomic():\n            city = City.create(name='Topeka')\n            venue1 = Venue.create(name='House', city=city, city_n=city)\n            venue2 = Venue.create(name='Nowhere', city=city, city_n=None)\n\n            event1 = Event.create(name='House Party', venue=venue1)\n            event2 = Event.create(name='Holiday')\n            event3 = Event.create(name='Nowhere Party', venue=venue2)\n\n        with self.assertQueryCount(1):\n            query = (Event\n                     .select(Event, Venue, City)\n                     .join(Venue, JOIN.LEFT_OUTER)\n                     .join(City, JOIN.LEFT_OUTER, on=Venue.city)\n                     .order_by(Event.id))\n\n            # Here we have two left-outer joins, and the second Event\n            # (\"Holiday\"), does not have an associated Venue (hence, no City).\n            # Peewee would attach an empty Venue() model to the event, however.\n            # It did this since we are selecting from Venue/City and Venue is\n            # an intermediary model. It is more correct for Event.venue to be\n            # None in this case. This is now patched / fixed.\n            r = [(e.name, e.venue is not None and e.venue.city.name or None)\n                 for e in query]\n            self.assertEqual(r, [\n                ('House Party', 'Topeka'),\n                ('Holiday', None),\n                ('Nowhere Party', 'Topeka')])\n\n        with self.assertQueryCount(1):\n            query = (Event\n                     .select(Event, Venue, City)\n                     .join(Venue, JOIN.INNER)\n                     .join(City, JOIN.LEFT_OUTER, on=Venue.city_n)\n                     .order_by(Event.id))\n\n            # Here we have an inner join and a left-outer join. The furthest\n            # object (City) will be NULL for the \"Nowhere Party\". Make sure\n            # that the object is left as None and not populated with an empty\n            # City instance.\n            accum = []\n            for event in query:\n                city_name = event.venue.city_n and event.venue.city_n.name\n                accum.append((event.name, event.venue.name, city_name))\n\n            self.assertEqual(accum, [\n                ('House Party', 'House', 'Topeka'),\n                ('Nowhere Party', 'Nowhere', None)])\n\n    @requires_models(Relationship, Person)\n    def test_join_same_model_twice(self):\n        d = datetime.date(2010, 1, 1)\n        huey = Person.create(first='huey', last='cat', dob=d)\n        zaizee = Person.create(first='zaizee', last='cat', dob=d)\n        mickey = Person.create(first='mickey', last='dog', dob=d)\n        relationships = (\n            (huey, zaizee),\n            (zaizee, huey),\n            (mickey, huey),\n        )\n        for src, dest in relationships:\n            Relationship.create(from_person=src, to_person=dest)\n\n        PA = Person.alias()\n        with self.assertQueryCount(1):\n            query = (Relationship\n                     .select(Relationship, Person, PA)\n                     .join(Person, on=Relationship.from_person)\n                     .switch(Relationship)\n                     .join(PA, on=Relationship.to_person)\n                     .order_by(Relationship.id))\n            results = [(r.from_person.first, r.to_person.first) for r in query]\n\n        self.assertEqual(results, [\n            ('huey', 'zaizee'),\n            ('zaizee', 'huey'),\n            ('mickey', 'huey')])\n\n    @requires_models(User, Tweet)\n    def test_join_to_dict(self):\n        huey = self.add_user('huey')\n        mickey = self.add_user('mickey')\n        self.add_tweets(huey, 'meow', 'hiss', 'purr')\n        self.add_tweets(mickey, 'woof')\n\n        with self.assertQueryCount(1):\n            q = Select((User,), (User.id, User.username,))\n            query = (Tweet\n                     .select(Tweet.content, q.c.username)\n                     .join(q, on=(Tweet.user == q.c.id), attr='u')\n                     .order_by(q.c.username, Tweet.content))\n            self.assertSQL(query, (\n                'SELECT \"t1\".\"content\", \"t2\".\"username\" FROM \"tweet\" AS \"t1\" '\n                'INNER JOIN (SELECT \"t3\".\"id\", \"t3\".\"username\" FROM \"users\" '\n                'AS \"t3\") AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n                'ORDER BY \"t2\".\"username\", \"t1\".\"content\"'), [])\n\n            tweets = list(query)\n            self.assertEqual([(t.content, t.u) for t in tweets], [\n                ('hiss', {'username': 'huey'}),\n                ('meow', {'username': 'huey'}),\n                ('purr', {'username': 'huey'}),\n                ('woof', {'username': 'mickey'})])\n\n    @requires_models(User)\n    def test_peek(self):\n        for username in ('huey', 'mickey', 'zaizee'):\n            self.add_user(username)\n\n        with self.assertQueryCount(1):\n            query = User.select(User.username).order_by(User.username).dicts()\n            self.assertEqual(query.peek(n=1), {'username': 'huey'})\n            self.assertEqual(query.peek(n=2), [{'username': 'huey'},\n                                               {'username': 'mickey'}])\n\n    @requires_models(User)\n    def test_first(self):\n        for u in 'abc':\n            self.add_user(u)\n\n        # Multiple calls to first() do not result in multiple executions.\n        with self.assertQueryCount(1):\n            q = User.select().order_by(User.username)\n            self.assertEqual(q.first().username, 'a')\n            self.assertEqual(q.first().username, 'a')\n\n    @requires_models(User, Tweet, Favorite)\n    def test_multi_join(self):\n        u1 = User.create(username='u1')\n        u2 = User.create(username='u2')\n        u3 = User.create(username='u3')\n        t1_1 = Tweet.create(user=u1, content='t1-1')\n        t1_2 = Tweet.create(user=u1, content='t1-2')\n        t2_1 = Tweet.create(user=u2, content='t2-1')\n        t2_2 = Tweet.create(user=u2, content='t2-2')\n        favorites = ((u1, t2_1),\n                     (u1, t2_2),\n                     (u2, t1_1),\n                     (u3, t1_2),\n                     (u3, t2_2))\n        for user, tweet in favorites:\n            Favorite.create(user=user, tweet=tweet)\n\n        TweetUser = User.alias('u2')\n\n        with self.assertQueryCount(1):\n            query = (Favorite\n                     .select(Favorite.id,\n                             Tweet.content,\n                             User.username,\n                             TweetUser.username)\n                     .join(Tweet)\n                     .join(TweetUser, on=(Tweet.user == TweetUser.id))\n                     .switch(Favorite)\n                     .join(User)\n                     .order_by(Tweet.content, Favorite.id))\n            self.assertSQL(query, (\n                'SELECT '\n                '\"t1\".\"id\", \"t2\".\"content\", \"t3\".\"username\", \"u2\".\"username\" '\n                'FROM \"favorite\" AS \"t1\" '\n                'INNER JOIN \"tweet\" AS \"t2\" ON (\"t1\".\"tweet_id\" = \"t2\".\"id\") '\n                'INNER JOIN \"users\" AS \"u2\" ON (\"t2\".\"user_id\" = \"u2\".\"id\") '\n                'INNER JOIN \"users\" AS \"t3\" ON (\"t1\".\"user_id\" = \"t3\".\"id\") '\n                'ORDER BY \"t2\".\"content\", \"t1\".\"id\"'), [])\n\n            accum = [(f.tweet.user.username, f.tweet.content, f.user.username)\n                     for f in query]\n            self.assertEqual(accum, [\n                ('u1', 't1-1', 'u2'),\n                ('u1', 't1-2', 'u3'),\n                ('u2', 't2-1', 'u1'),\n                ('u2', 't2-2', 'u1'),\n                ('u2', 't2-2', 'u3')])\n\n        res = query.count()\n        self.assertEqual(res, 5)\n\n    def _create_user_tweets(self):\n        data = (('huey', ('meow', 'purr', 'hiss')),\n                ('zaizee', ()),\n                ('mickey', ('woof', 'grr')))\n\n        with self.database.atomic():\n            ts = int(time.time())\n            for username, tweets in data:\n                user = User.create(username=username)\n                for tweet in tweets:\n                    Tweet.create(user=user, content=tweet, timestamp=ts)\n                    ts += 1\n\n    @requires_models(User, Tweet)\n    def test_join_subquery(self):\n        self._create_user_tweets()\n\n        # Select note user and timestamp of most recent tweet.\n        with self.assertQueryCount(1):\n            TA = Tweet.alias()\n            max_q = (TA\n                     .select(TA.user, fn.MAX(TA.timestamp).alias('max_ts'))\n                     .group_by(TA.user)\n                     .alias('max_q'))\n\n            predicate = ((Tweet.user == max_q.c.user_id) &\n                         (Tweet.timestamp == max_q.c.max_ts))\n            latest = (Tweet\n                      .select(Tweet.user, Tweet.content, Tweet.timestamp)\n                      .join(max_q, on=predicate)\n                      .alias('latest'))\n\n            query = (User\n                     .select(User, latest.c.content, latest.c.timestamp)\n                     .join(latest, on=(User.id == latest.c.user_id)))\n\n            data = [(user.username, user.tweet.content) for user in query]\n\n        # Failing on travis-ci...old SQLite?\n        if not IS_SQLITE_OLD:\n            self.assertEqual(data, [\n                ('huey', 'hiss'),\n                ('mickey', 'grr')])\n\n        with self.assertQueryCount(1):\n            query = (Tweet\n                     .select(Tweet, User)\n                     .join(max_q, on=predicate)\n                     .switch(Tweet)\n                     .join(User))\n            data = [(note.user.username, note.content) for note in query]\n\n        self.assertEqual(data, [\n            ('huey', 'hiss'),\n            ('mickey', 'grr')])\n\n    @requires_models(User, Tweet)\n    def test_join_subquery_2(self):\n        self._create_user_tweets()\n\n        with self.assertQueryCount(1):\n            users = (User\n                     .select(User.id, User.username)\n                     .where(User.username.in_(['huey', 'zaizee'])))\n            query = (Tweet\n                     .select(Tweet.content.alias('content'),\n                             users.c.username.alias('username'))\n                     .join(users, on=(Tweet.user == users.c.id))\n                     .order_by(Tweet.id))\n\n            self.assertSQL(query, (\n                'SELECT \"t1\".\"content\" AS \"content\", '\n                '\"t2\".\"username\" AS \"username\"'\n                ' FROM \"tweet\" AS \"t1\" '\n                'INNER JOIN (SELECT \"t3\".\"id\", \"t3\".\"username\" '\n                'FROM \"users\" AS \"t3\" '\n                'WHERE (\"t3\".\"username\" IN (?, ?))) AS \"t2\" '\n                'ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n                'ORDER BY \"t1\".\"id\"'), ['huey', 'zaizee'])\n\n            results = [(t.content, t.user.username) for t in query]\n            self.assertEqual(results, [\n                ('meow', 'huey'),\n                ('purr', 'huey'),\n                ('hiss', 'huey')])\n\n    @skip_if(IS_SQLITE_OLD or (IS_MYSQL and not IS_MYSQL_ADVANCED_FEATURES))\n    @requires_models(User, Tweet)\n    def test_join_subquery_cte(self):\n        self._create_user_tweets()\n\n        cte = (User\n               .select(User.id, User.username)\n               .where(User.username.in_(['huey', 'zaizee']))\\\n               .cte('cats'))\n\n        with self.assertQueryCount(1):\n            # Attempt join with subquery as common-table expression.\n            query = (Tweet\n                     .select(Tweet.content, cte.c.username)\n                     .join(cte, on=(Tweet.user == cte.c.id))\n                     .order_by(Tweet.id)\n                     .with_cte(cte))\n            self.assertSQL(query, (\n                'WITH \"cats\" AS ('\n                'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n                'WHERE (\"t1\".\"username\" IN (?, ?))) '\n                'SELECT \"t2\".\"content\", \"cats\".\"username\" FROM \"tweet\" AS \"t2\" '\n                'INNER JOIN \"cats\" ON (\"t2\".\"user_id\" = \"cats\".\"id\") '\n                'ORDER BY \"t2\".\"id\"'), ['huey', 'zaizee'])\n\n            self.assertEqual([t.content for t in query],\n                             ['meow', 'purr', 'hiss'])\n\n    @skip_if(IS_MYSQL)  # MariaDB does not support LIMIT in subqueries!\n    @requires_models(User)\n    def test_subquery_emulate_window(self):\n        # We have duplicated users. Select a maximum of 2 instances of the\n        # username.\n        name2count = {\n            'beanie': 6,\n            'huey': 5,\n            'mickey': 3,\n            'pipey': 1,\n            'zaizee': 4}\n        names = []\n        for name, count in sorted(name2count.items()):\n            names += [name] * count\n        User.insert_many([(i, n) for i, n in enumerate(names, 1)],\n                         [User.id, User.username]).execute()\n\n        # The results we are trying to obtain.\n        expected = [\n            ('beanie', 1), ('beanie', 2),\n            ('huey', 7), ('huey', 8),\n            ('mickey', 12), ('mickey', 13),\n            ('pipey', 15),\n            ('zaizee', 16), ('zaizee', 17)]\n\n        with self.assertQueryCount(1):\n            # Using a self-join.\n            UA = User.alias()\n            query = (User\n                     .select(User.username, UA.id)\n                     .join(UA, on=((UA.username == User.username) &\n                                   (UA.id >= User.id)))\n                     .group_by(User.username, UA.id)\n                     .having(fn.COUNT(UA.id) < 3)\n                     .order_by(User.username, UA.id))\n            self.assertEqual(query.tuples()[:], expected)\n\n        with self.assertQueryCount(1):\n            # Using a correlated subquery.\n            subq = (UA\n                    .select(UA.id)\n                    .where(User.username == UA.username)\n                    .order_by(UA.id)\n                    .limit(2))\n            query = (User\n                     .select(User.username, User.id)\n                     .where(User.id.in_(subq.alias('subq')))\n                     .order_by(User.username, User.id))\n            self.assertEqual(query.tuples()[:], expected)\n\n    @requires_models(User, Tweet)\n    def test_subquery_alias_selection(self):\n        data = (\n            ('huey', ('meow', 'hiss', 'purr')),\n            ('mickey', ('woof', 'bark')),\n            ('zaizee', ()))\n        with self.database.atomic():\n            for username, tweets in data:\n                user = User.create(username=username)\n                for tweet in tweets:\n                    Tweet.create(user=user, content=tweet)\n\n        with self.assertQueryCount(1):\n            subq = (Tweet\n                    .select(fn.COUNT(Tweet.id))\n                    .where(Tweet.user == User.id))\n            query = (User\n                     .select(User.username, subq.alias('tweet_count'))\n                     .order_by(User.id))\n            self.assertEqual([(u.username, u.tweet_count) for u in query], [\n                ('huey', 3),\n                ('mickey', 2),\n                ('zaizee', 0)])\n\n    @requires_pglike\n    @requires_models(User)\n    def test_join_on_valueslist(self):\n        for username in ('huey', 'mickey', 'zaizee'):\n            User.create(username=username)\n\n        vl = ValuesList([('huey',), ('zaizee',)], columns=['username'])\n        with self.assertQueryCount(1):\n            query = (User\n                     .select(vl.c.username)\n                     .join(vl, on=(User.username == vl.c.username))\n                     .order_by(vl.c.username.desc()))\n            self.assertEqual([u.username for u in query], ['zaizee', 'huey'])\n\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL or IS_CRDB)\n    @requires_models(User)\n    def test_multi_update(self):\n        data = [(i, 'u%s' % i) for i in range(1, 4)]\n        User.insert_many(data, fields=[User.id, User.username]).execute()\n\n        data = [(i, 'u%sx' % i) for i in range(1, 3)]\n        vl = ValuesList(data)\n        cte = vl.select().cte('uv', columns=('id', 'username'))\n        subq = cte.select(cte.c.username).where(cte.c.id == User.id)\n        res = (User\n               .update(username=subq)\n               .where(User.id.in_(cte.select(cte.c.id)))\n               .with_cte(cte)\n               .execute())\n        query = User.select().order_by(User.id)\n        self.assertEqual([(u.id, u.username) for u in query], [\n            (1, 'u1x'),\n            (2, 'u2x'),\n            (3, 'u3')])\n\n    @requires_models(User, Tweet)\n    def test_insert_query_value(self):\n        huey = self.add_user('huey')\n        query = User.select(User.id).where(User.username == 'huey')\n        tid = Tweet.insert(content='meow', user=query).execute()\n        tweet = Tweet[tid]\n        self.assertEqual(tweet.user.id, huey.id)\n        self.assertEqual(tweet.user.username, 'huey')\n\n    @skip_if(IS_SQLITE and not IS_SQLITE_9, 'requires sqlite >= 3.9')\n    @requires_models(Register)\n    def test_compound_select(self):\n        for i in range(10):\n            Register.create(value=i)\n\n        q1 = Register.select().where(Register.value < 2)\n        q2 = Register.select().where(Register.value > 7)\n        c1 = (q1 | q2).order_by(SQL('2'))\n\n        self.assertSQL(c1, (\n            'SELECT \"t1\".\"id\", \"t1\".\"value\" FROM \"register\" AS \"t1\" '\n            'WHERE (\"t1\".\"value\" < ?) UNION '\n            'SELECT \"t2\".\"id\", \"t2\".\"value\" FROM \"register\" AS \"t2\" '\n            'WHERE (\"t2\".\"value\" > ?) ORDER BY 2'), [2, 7])\n\n        self.assertEqual([row.value for row in c1], [0, 1, 8, 9],\n                         [row.__data__ for row in c1])\n        self.assertEqual(c1.count(), 4)\n\n        q3 = Register.select().where(Register.value == 5)\n        c2 = (c1.order_by() | q3).order_by(SQL('2'))\n\n        self.assertSQL(c2, (\n            'SELECT \"t1\".\"id\", \"t1\".\"value\" FROM \"register\" AS \"t1\" '\n            'WHERE (\"t1\".\"value\" < ?) UNION '\n            'SELECT \"t2\".\"id\", \"t2\".\"value\" FROM \"register\" AS \"t2\" '\n            'WHERE (\"t2\".\"value\" > ?) UNION '\n            'SELECT \"t3\".\"id\", \"t3\".\"value\" FROM \"register\" AS \"t3\" '\n            'WHERE (\"t3\".\"value\" = ?) ORDER BY 2'), [2, 7, 5])\n\n        self.assertEqual([row.value for row in c2], [0, 1, 5, 8, 9])\n        self.assertEqual(c2.count(), 5)\n\n    @requires_models(User, Tweet)\n    def test_union_column_resolution(self):\n        u1 = User.create(id=1, username='u1')\n        u2 = User.create(id=2, username='u2')\n        q1 = User.select().where(User.id == 1)\n        q2 = User.select()\n        union = q1 | q2\n        self.assertSQL(union, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" = ?) '\n            'UNION '\n            'SELECT \"t2\".\"id\", \"t2\".\"username\" FROM \"users\" AS \"t2\"'), [1])\n\n        results = [(user.id, user.username) for user in union]\n        self.assertEqual(sorted(results), [\n            (1, 'u1'),\n            (2, 'u2')])\n\n        t1_1 = Tweet.create(id=1, user=u1, content='u1-t1')\n        t1_2 = Tweet.create(id=2, user=u1, content='u1-t2')\n        t2_1 = Tweet.create(id=3, user=u2, content='u2-t1')\n\n        with self.assertQueryCount(1):\n            q1 = Tweet.select(Tweet, User).join(User).where(User.id == 1)\n            q2 = Tweet.select(Tweet, User).join(User)\n            union = q1 | q2\n\n            self.assertSQL(union, (\n                'SELECT \"t1\".\"id\", \"t1\".\"user_id\", \"t1\".\"content\", '\n                '\"t1\".\"timestamp\", \"t2\".\"id\", \"t2\".\"username\" '\n                'FROM \"tweet\" AS \"t1\" '\n                'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n                'WHERE (\"t2\".\"id\" = ?) '\n                'UNION '\n                'SELECT \"t3\".\"id\", \"t3\".\"user_id\", \"t3\".\"content\", '\n                '\"t3\".\"timestamp\", \"t4\".\"id\", \"t4\".\"username\" '\n                'FROM \"tweet\" AS \"t3\" '\n                'INNER JOIN \"users\" AS \"t4\" ON (\"t3\".\"user_id\" = \"t4\".\"id\")'),\n                [1])\n\n            results = [(t.id, t.content, t.user.username) for t in union]\n            self.assertEqual(sorted(results), [\n                (1, 'u1-t1', 'u1'),\n                (2, 'u1-t2', 'u1'),\n                (3, 'u2-t1', 'u2')])\n\n        with self.assertQueryCount(1):\n            union_flat = (q1 | q2).objects()\n            results = list(results)\n            results = [(t.id, t.content, t.username, t.id_2)\n                       for t in union_flat]\n            self.assertEqual(sorted(results), [\n                (1, 'u1-t1', 'u1', 1),\n                (2, 'u1-t2', 'u1', 1),\n                (3, 'u2-t1', 'u2', 2)])\n\n    @requires_models(User, Tweet)\n    def test_compound_select_as_subquery(self):\n        with self.database.atomic():\n            for i in range(5):\n                user = User.create(username='u%s' % i)\n                for j in range(i * 2):\n                    Tweet.create(user=user, content='t%s-%s' % (i, j))\n\n        q1 = (Tweet\n              .select(Tweet.id, Tweet.content, User.username)\n              .join(User)\n              .where(User.username == 'u3'))\n        q2 = (Tweet\n              .select(Tweet.id, Tweet.content, User.username)\n              .join(User)\n              .where(User.username.in_(['u2', 'u4'])))\n        union = (q1 | q2)\n\n        q = (union\n             .select_from(union.c.username, fn.COUNT(union.c.id).alias('ct'))\n             .group_by(union.c.username)\n             .order_by(fn.COUNT(union.c.id).desc())\n             .dicts())\n        self.assertEqual(list(q), [\n            {'username': 'u4', 'ct': 8},\n            {'username': 'u3', 'ct': 6},\n            {'username': 'u2', 'ct': 4}])\n\n    @requires_models(User, Tweet)\n    def test_union_with_join(self):\n        u1, u2 = [User.create(username='u%s' % i) for i in (1, 2)]\n        for u, ts in ((u1, ('t1', 't2')), (u2, ('t1',))):\n            for t in ts:\n                Tweet.create(user=u, content='%s-%s' % (u.username, t))\n\n        with self.assertQueryCount(1):\n            q1 = (User\n                  .select(User, Tweet)\n                  .join(Tweet, on=(Tweet.user == User.id).alias('foo')))\n            q2 = (User\n                  .select(User, Tweet)\n                  .join(Tweet, on=(Tweet.user == User.id).alias('foo')))\n\n            self.assertEqual(\n                sorted([(user.username, user.foo.content) for user in q1]),\n                [('u1', 'u1-t1'), ('u1', 'u1-t2'), ('u2', 'u2-t1')])\n\n        with self.assertQueryCount(1):\n            uq = q1.union_all(q2)\n            result = [(user.username, user.foo.content) for user in uq]\n            self.assertEqual(sorted(result), [\n                ('u1', 'u1-t1'),\n                ('u1', 'u1-t1'),\n                ('u1', 'u1-t2'),\n                ('u1', 'u1-t2'),\n                ('u2', 'u2-t1'),\n                ('u2', 'u2-t1'),\n            ])\n\n    @skip_if(IS_SQLITE_OLD or (IS_MYSQL and not IS_MYSQL_ADVANCED_FEATURES))\n    @requires_models(User)\n    def test_union_cte(self):\n        with self.database.atomic():\n            (User\n             .insert_many({'username': 'u%s' % i} for i in range(10))\n             .execute())\n\n        lhs = User.select().where(User.username.in_(['u1', 'u3']))\n        rhs = User.select().where(User.username.in_(['u5', 'u7']))\n        u_cte = (lhs | rhs).cte('users_union')\n\n        query = (User\n                 .select(User.username)\n                 .join(u_cte, on=(User.id == u_cte.c.id))\n                 .where(User.username.in_(['u1', 'u7']))\n                 .with_cte(u_cte))\n        self.assertEqual(sorted([u.username for u in query]), ['u1', 'u7'])\n\n    @requires_models(Category)\n    def test_self_referential_fk(self):\n        self.assertTrue(Category.parent.rel_model is Category)\n\n        root = Category.create(name='root')\n        c1 = Category.create(parent=root, name='child-1')\n        c2 = Category.create(parent=root, name='child-2')\n\n        with self.assertQueryCount(1):\n            Parent = Category.alias('p')\n            query = (Category\n                     .select(\n                         Parent.name,\n                         Category.name)\n                     .where(Category.parent == root)\n                     .order_by(Category.name))\n            query = query.join(Parent, on=(Category.parent == Parent.name))\n            c1_db, c2_db = list(query)\n\n            self.assertEqual(c1_db.name, 'child-1')\n            self.assertEqual(c1_db.parent.name, 'root')\n            self.assertEqual(c2_db.name, 'child-2')\n            self.assertEqual(c2_db.parent.name, 'root')\n\n    @requires_models(Category)\n    def test_empty_joined_instance(self):\n        root = Category.create(name='a')\n        c1 = Category.create(name='c1', parent=root)\n        c2 = Category.create(name='c2', parent=root)\n\n        with self.assertQueryCount(1):\n            Parent = Category.alias('p')\n            query = (Category\n                     .select(Category, Parent)\n                     .join(Parent, JOIN.LEFT_OUTER,\n                           on=(Category.parent == Parent.name))\n                     .order_by(Category.name))\n            result = [(category.name, category.parent is None)\n                      for category in query]\n\n        self.assertEqual(result, [('a', True), ('c1', False), ('c2', False)])\n\n    @requires_models(User, Tweet)\n    def test_from_multi_table(self):\n        self.add_tweets(self.add_user('huey'), 'meow', 'hiss', 'purr')\n        self.add_tweets(self.add_user('mickey'), 'woof', 'wheeze')\n\n        with self.assertQueryCount(1):\n            query = (Tweet\n                     .select(Tweet, User)\n                     .from_(Tweet, User)\n                     .where(\n                         (Tweet.user == User.id) &\n                         (User.username == 'huey'))\n                     .order_by(Tweet.id)\n                     .dicts())\n\n            self.assertEqual([t['content'] for t in query],\n                             ['meow', 'hiss', 'purr'])\n            self.assertEqual([t['username'] for t in query],\n                             ['huey', 'huey', 'huey'])\n\n    @requires_models(Point)\n    def test_subquery_in_select_expression(self):\n        for x, y in ((1, 1), (1, 2), (10, 10), (10, 20)):\n            Point.create(x=x, y=y)\n\n        with self.assertQueryCount(1):\n            PA = Point.alias('pa')\n            subq = PA.select(fn.SUM(PA.y)).where(PA.x == Point.x)\n            query = (Point\n                     .select(Point.x, Point.y, subq.alias('sy'))\n                     .order_by(Point.x, Point.y))\n            self.assertEqual(list(query.tuples()), [\n                (1, 1, 3),\n                (1, 2, 3),\n                (10, 10, 30),\n                (10, 20, 30)])\n\n        with self.assertQueryCount(1):\n            query = (Point\n                     .select(Point.x, (Point.y + subq).alias('sy'))\n                     .order_by(Point.x, Point.y))\n            self.assertEqual(list(query.tuples()), [\n                (1, 4), (1, 5),\n                (10, 40), (10, 50)])\n\n    @requires_models(User, Tweet)\n    def test_filtering(self):\n        with self.database.atomic():\n            huey = self.add_user('huey')\n            mickey = self.add_user('mickey')\n            self.add_tweets(huey, 'meow', 'hiss', 'purr')\n            self.add_tweets(mickey, 'woof', 'wheeze')\n\n        with self.assertQueryCount(1):\n            query = Tweet.filter(user__username='huey').order_by(Tweet.content)\n            self.assertEqual([row.content for row in query],\n                             ['hiss', 'meow', 'purr'])\n\n        with self.assertQueryCount(1):\n            query = User.filter(tweets__content__ilike='w%')\n            self.assertEqual([user.username for user in query],\n                             ['mickey', 'mickey'])\n\n    def test_deferred_fk(self):\n        class Note(TestModel):\n            foo = DeferredForeignKey('Foo', backref='notes')\n\n        class Foo(TestModel):\n            note = ForeignKeyField(Note)\n\n        self.assertTrue(Note.foo.rel_model is Foo)\n        self.assertTrue(Foo.note.rel_model is Note)\n        f = Foo(id=1337)\n        self.assertSQL(f.notes, (\n            'SELECT \"t1\".\"id\", \"t1\".\"foo_id\" FROM \"note\" AS \"t1\" '\n            'WHERE (\"t1\".\"foo_id\" = ?)'), [1337])\n\n    def test_deferred_fk_dependency_graph(self):\n        class AUser(TestModel):\n            foo = DeferredForeignKey('Tweet')\n        class ZTweet(TestModel):\n            user = ForeignKeyField(AUser, backref='ztweets')\n\n        self.assertEqual(sort_models([AUser, ZTweet]), [AUser, ZTweet])\n\n    def test_table_schema(self):\n        class Schema(TestModel):\n            pass\n\n        self.assertTrue(Schema._meta.schema is None)\n        self.assertSQL(Schema.select(), (\n            'SELECT \"t1\".\"id\" FROM \"schema\" AS \"t1\"'), [])\n\n        Schema._meta.schema = 'test'\n        self.assertSQL(Schema.select(), (\n            'SELECT \"t1\".\"id\" FROM \"test\".\"schema\" AS \"t1\"'), [])\n\n        Schema._meta.schema = 'another'\n        self.assertSQL(Schema.select(), (\n            'SELECT \"t1\".\"id\" FROM \"another\".\"schema\" AS \"t1\"'), [])\n\n    @requires_models(User)\n    def test_noop(self):\n        query = User.noop()\n        self.assertEqual(list(query), [])\n\n    @requires_models(User)\n    def test_iteration(self):\n        self.assertEqual(list(User), [])\n        self.assertEqual(len(User), 0)\n        self.assertTrue(User)\n\n        User.insert_many((['charlie'], ['huey']), [User.username]).execute()\n        self.assertEqual(sorted(u.username for u in User), ['charlie', 'huey'])\n        self.assertEqual(len(User), 2)\n        self.assertTrue(User)\n\n    @requires_models(User)\n    def test_iterator(self):\n        users = ['charlie', 'huey', 'zaizee']\n        with self.database.atomic():\n            for username in users:\n                User.create(username=username)\n\n        with self.assertQueryCount(1):\n            query = User.select().order_by(User.username).iterator()\n            self.assertEqual([u.username for u in query], users)\n\n            self.assertEqual(list(query), [])\n\n    @requires_models(User)\n    def test_select_count(self):\n        users = [self.add_user(u) for u in ('huey', 'charlie', 'mickey')]\n        self.assertEqual(User.select().count(), 3)\n\n        qr = User.select().execute()\n        self.assertEqual(qr.count, 0)\n\n        list(qr)\n        self.assertEqual(qr.count, 3)\n\n    @requires_models(User)\n    def test_batch_commit(self):\n        commit_method = self.database.commit\n\n        def assertBatch(n_rows, batch_size, n_commits):\n            User.delete().execute()\n            user_data = [{'username': 'u%s' % i} for i in range(n_rows)]\n            with mock.patch.object(self.database, 'commit') as mock_commit:\n                mock_commit.side_effect = commit_method\n                for row in self.database.batch_commit(user_data, batch_size):\n                    User.create(**row)\n\n                self.assertEqual(mock_commit.call_count, n_commits)\n                self.assertEqual(User.select().count(), n_rows)\n\n        assertBatch(6, 1, 6)\n        assertBatch(6, 2, 3)\n        assertBatch(6, 3, 2)\n        assertBatch(6, 4, 2)\n        assertBatch(6, 6, 1)\n        assertBatch(6, 7, 1)\n\n\nclass TestRaw(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User]\n\n    def test_raw(self):\n        with self.database.atomic():\n            for username in ('charlie', 'chuck', 'huey', 'zaizee'):\n                User.create(username=username)\n\n        query = (User\n                 .raw('SELECT username, SUBSTR(username, 1, 1) AS first '\n                      'FROM users '\n                      'WHERE SUBSTR(username, 1, 1) = ? '\n                      'ORDER BY username DESC', 'c'))\n        self.assertEqual([(row.username, row.first) for row in query],\n                         [('chuck', 'c'), ('charlie', 'c')])\n\n    def test_raw_iterator(self):\n        (User\n         .insert_many([('charlie',), ('huey',)], fields=[User.username])\n         .execute())\n\n        with self.assertQueryCount(1):\n            query = User.raw('SELECT * FROM users ORDER BY id')\n            results = [user.username for user in query.iterator()]\n            self.assertEqual(results, ['charlie', 'huey'])\n\n            # Since we used iterator(), the results were not cached.\n            self.assertEqual([u.username for u in query], [])\n\n\nclass TestDeleteInstance(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User, Account, Tweet, Favorite]\n\n    def setUp(self):\n        super(TestDeleteInstance, self).setUp()\n        with self.database.atomic():\n            huey = User.create(username='huey')\n            acct = Account.create(user=huey, email='huey@meow.com')\n            for content in ('meow', 'purr'):\n                Tweet.create(user=huey, content=content)\n            mickey = User.create(username='mickey')\n            woof = Tweet.create(user=mickey, content='woof')\n            Favorite.create(user=huey, tweet=woof)\n            Favorite.create(user=mickey, tweet=Tweet.create(user=huey,\n                                                            content='hiss'))\n\n    def test_delete_instance_recursive(self):\n        huey = User.get(User.username == 'huey')\n        a = []\n        for d in huey.dependencies():\n            a.append(d)\n        with self.assertQueryCount(5):\n            huey.delete_instance(recursive=True)\n\n        self.assertHistory(5, [\n            ('DELETE FROM \"favorite\" WHERE (\"favorite\".\"user_id\" = ?)',\n             [huey.id]),\n            ('DELETE FROM \"favorite\" WHERE ('\n             '\"favorite\".\"tweet_id\" IN ('\n             'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" WHERE ('\n             '\"t1\".\"user_id\" = ?)))', [huey.id]),\n            ('DELETE FROM \"tweet\" WHERE (\"tweet\".\"user_id\" = ?)', [huey.id]),\n            ('UPDATE \"account\" SET \"user_id\" = ? '\n             'WHERE (\"account\".\"user_id\" = ?)',\n             [None, huey.id]),\n            ('DELETE FROM \"users\" WHERE (\"users\".\"id\" = ?)', [huey.id]),\n        ])\n\n        # Only one user left.\n        self.assertEqual(User.select().count(), 1)\n\n        # Huey's account has had the FK cleared out.\n        acct = Account.get(Account.email == 'huey@meow.com')\n        self.assertTrue(acct.user is None)\n\n        # Huey owned a favorite and one of huey's tweets was the other fav.\n        self.assertEqual(Favorite.select().count(), 0)\n\n        # The only tweet left is mickey's.\n        self.assertEqual(Tweet.select().count(), 1)\n        tweet = Tweet.get()\n        self.assertEqual(tweet.content, 'woof')\n\n    def test_delete_nullable(self):\n        huey = User.get(User.username == 'huey')\n        # Favorite -> Tweet -> User (other users' favorites of huey's tweets)\n        # Favorite -> User (huey's favorite tweets)\n        # Account -> User (huey's account)\n        # User ... for a total of 5. Favorite x2, Tweet, Account, User.\n        with self.assertQueryCount(5):\n            huey.delete_instance(recursive=True, delete_nullable=True)\n\n        # Get the last 5 delete queries.\n        self.assertHistory(5, [\n            ('DELETE FROM \"favorite\" WHERE (\"favorite\".\"user_id\" = ?)',\n             [huey.id]),\n            ('DELETE FROM \"favorite\" WHERE ('\n             '\"favorite\".\"tweet_id\" IN ('\n             'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" WHERE ('\n             '\"t1\".\"user_id\" = ?)))', [huey.id]),\n            ('DELETE FROM \"tweet\" WHERE (\"tweet\".\"user_id\" = ?)', [huey.id]),\n            ('DELETE FROM \"account\" WHERE (\"account\".\"user_id\" = ?)',\n             [huey.id]),\n            ('DELETE FROM \"users\" WHERE (\"users\".\"id\" = ?)', [huey.id]),\n        ])\n\n        self.assertEqual(User.select().count(), 1)\n        self.assertEqual(Account.select().count(), 0)\n        self.assertEqual(Favorite.select().count(), 0)\n\n        self.assertEqual(Tweet.select().count(), 1)\n        tweet = Tweet.get()\n        self.assertEqual(tweet.content, 'woof')\n\n\ndef incrementer():\n    d = {'value': 0}\n    def increment():\n        d['value'] += 1\n        return d['value']\n    return increment\n\nclass AutoCounter(TestModel):\n    counter = IntegerField(default=incrementer())\n    control = IntegerField(default=1)\n\n\nclass TestDefaultDirtyBehavior(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [AutoCounter]\n\n    def tearDown(self):\n        super(TestDefaultDirtyBehavior, self).tearDown()\n        AutoCounter._meta.only_save_dirty = False\n\n    def test_default_dirty(self):\n        AutoCounter._meta.only_save_dirty = True\n\n        ac = AutoCounter()\n        ac.save()\n        self.assertEqual(ac.counter, 1)\n        self.assertEqual(ac.control, 1)\n\n        ac_db = AutoCounter.get((AutoCounter.counter == 1) &\n                                (AutoCounter.control == 1))\n        self.assertEqual(ac_db.counter, 1)\n        self.assertEqual(ac_db.control, 1)\n\n        # No changes.\n        self.assertFalse(ac_db.save())\n\n        ac = AutoCounter.create()\n        self.assertEqual(ac.counter, 2)\n        self.assertEqual(ac.control, 1)\n\n        AutoCounter._meta.only_save_dirty = False\n\n        ac = AutoCounter()\n        self.assertEqual(ac.counter, 3)\n        self.assertEqual(ac.control, 1)\n        ac.save()\n\n        ac_db = AutoCounter.get(AutoCounter.id == ac.id)\n        self.assertEqual(ac_db.counter, 3)\n\n    @requires_models(Person)\n    def test_save_only_dirty(self):\n        today = datetime.date.today()\n        try:\n            for only_save_dirty in (False, True):\n                Person._meta.only_save_dirty = only_save_dirty\n\n                p = Person.create(first='f', last='l', dob=today)\n                p.first = 'f2'\n                p.last = 'l2'\n                p.save(only=[Person.first])\n                self.assertEqual(p.dirty_fields, [Person.last])\n                self.assertFalse('first' in p.dirty_field_names)\n                self.assertTrue('last' in p.dirty_field_names)\n\n                p_db = Person.get(Person.id == p.id)\n                self.assertEqual((p_db.first, p_db.last), ('f2', 'l'))\n\n                p.save()\n                self.assertEqual(p.dirty_fields, [])\n\n                p_db = Person.get(Person.id == p.id)\n                self.assertEqual((p_db.first, p_db.last), ('f2', 'l2'))\n\n                p.delete_instance()\n        finally:\n            # Reset only_save_dirty property for other tests.\n            Person._meta.only_save_dirty = False\n\n\nclass TestDefaultValues(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Sample, SampleMeta]\n\n    def test_default_present_on_insert(self):\n        # Although value is not specified, it has a default, which is included\n        # in the INSERT.\n        query = Sample.insert(counter=0)\n        self.assertSQL(query, (\n            'INSERT INTO \"sample\" (\"counter\", \"value\") '\n            'VALUES (?, ?)'), [0, 1.0])\n\n        # Default values are also included when doing bulk inserts.\n        query = Sample.insert_many([\n            {'counter': '0'},\n            {'counter': 1, 'value': 2},\n            {'counter': '2'}])\n        self.assertSQL(query, (\n            'INSERT INTO \"sample\" (\"counter\", \"value\") '\n            'VALUES (?, ?), (?, ?), (?, ?)'), [0, 1.0, 1, 2.0, 2, 1.0])\n\n        query = Sample.insert_many([(0,), (1, 2.)],\n                                   fields=[Sample.counter])\n        self.assertSQL(query, (\n            'INSERT INTO \"sample\" (\"counter\", \"value\") '\n            'VALUES (?, ?), (?, ?)'), [0, 1.0, 1, 2.0])\n\n    def test_default_present_on_create(self):\n        s = Sample.create(counter=3)\n        s_db = Sample.get(Sample.counter == 3)\n        self.assertEqual(s_db.value, 1.)\n\n    def test_defaults_from_cursor(self):\n        s = Sample.create(counter=1)\n        sm1 = SampleMeta.create(sample=s, value=1.)\n        sm2 = SampleMeta.create(sample=s, value=2.)\n\n        # Defaults are not present when doing a read query.\n        with self.assertQueryCount(1):\n            # Simple query.\n            query = (SampleMeta.select(SampleMeta.sample)\n                     .order_by(SampleMeta.value))\n            sm1_db, sm2_db = list(query)\n            self.assertIsNone(sm1_db.value)\n            self.assertIsNone(sm2_db.value)\n\n        with self.assertQueryCount(1):\n            # Join-graph query.\n            query = (SampleMeta\n                     .select(SampleMeta.sample,\n                             Sample.counter)\n                     .join(Sample)\n                     .order_by(SampleMeta.value))\n\n            sm1_db, sm2_db = list(query)\n            self.assertIsNone(sm1_db.value)\n            self.assertIsNone(sm2_db.value)\n            self.assertIsNone(sm1_db.sample.value)\n            self.assertIsNone(sm2_db.sample.value)\n            self.assertEqual(sm1_db.sample.counter, 1)\n            self.assertEqual(sm2_db.sample.counter, 1)\n\n\nclass TestFunctionCoerce(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Sample]\n\n    def test_coerce(self):\n        for i in range(3):\n            Sample.create(counter=i, value=i)\n\n        counter_group = fn.GROUP_CONCAT(Sample.counter).coerce(False)\n        query = Sample.select(counter_group.alias('counter'))\n        self.assertEqual(query.get().counter, '0,1,2')\n\n        query = Sample.select(counter_group.alias('counter_group'))\n        self.assertEqual(query.get().counter_group, '0,1,2')\n\n        query = Sample.select(counter_group)\n        self.assertEqual(query.scalar(), '0,1,2')\n\n    def test_scalar(self):\n        for i in range(4):\n            Sample.create(counter=i, value=i)\n        query = Sample.select(fn.SUM(Sample.counter).alias('total'))\n        self.assertEqual(query.scalar(), 6)\n        self.assertEqual(query.scalar(as_tuple=True), (6,))\n        self.assertEqual(query.scalar(as_dict=True), {'total': 6})\n\n        Sample.delete().execute()\n        self.assertTrue(query.scalar() is None)\n        self.assertEqual(query.scalar(as_tuple=True), (None,))\n        self.assertEqual(query.scalar(as_dict=True), {'total': None})\n\n    def test_safe_python_value(self):\n        for i in range(3):\n            Sample.create(counter=i, value=i)\n\n        counter_group = fn.GROUP_CONCAT(Sample.counter)\n        query = Sample.select(counter_group.alias('counter'))\n        self.assertEqual(query.get().counter, '0,1,2')\n        self.assertEqual(query.scalar(), '0,1,2')\n\n        query = Sample.select(counter_group.alias('counter_group'))\n        self.assertEqual(query.get().counter_group, '0,1,2')\n        self.assertEqual(query.scalar(), '0,1,2')\n\n    def test_conv_using_python_value(self):\n        for i in range(3):\n            Sample.create(counter=i, value=i)\n\n        counter = (fn\n                   .GROUP_CONCAT(Sample.counter)\n                   .python_value(lambda x: [int(i) for i in x.split(',')]))\n        query = Sample.select(counter.alias('counter'))\n        self.assertEqual(query.get().counter, [0, 1, 2])\n\n        query = Sample.select(counter.alias('counter_group'))\n        self.assertEqual(query.get().counter_group, [0, 1, 2])\n\n        query = Sample.select(counter)\n        self.assertEqual(query.scalar(), [0, 1, 2])\n\n    @requires_models(Category, Sample)\n    def test_no_coerce_count_avg(self):\n        for i in range(10):\n            Category.create(name=str(i))\n\n        # COUNT() does not result in the value being coerced.\n        query = Category.select(fn.COUNT(Category.name))\n        self.assertEqual(query.scalar(), 10)\n\n        # Force the value to be coerced using the field's db_value().\n        query = Category.select(fn.COUNT(Category.name).coerce(True))\n        self.assertEqual(query.scalar(), '10')\n\n        # Ensure avg over an integer field is returned as a float.\n        Sample.insert_many([(1, 0), (2, 0)]).execute()\n        query = Sample.select(fn.AVG(Sample.counter).alias('a'))\n        self.assertEqual(query.get().a, 1.5)\n\n\nclass TestJoinModelAlias(ModelTestCase):\n    data = (\n        ('huey', 'meow'),\n        ('huey', 'purr'),\n        ('zaizee', 'hiss'),\n        ('mickey', 'woof'))\n    requires = [User, Tweet]\n\n    def setUp(self):\n        super(TestJoinModelAlias, self).setUp()\n        users = {}\n        for pk, (username, tweet) in enumerate(self.data, 1):\n            if username not in users:\n                user = User.create(id=len(users) + 1, username=username)\n                users[username] = user\n            else:\n                user = users[username]\n            Tweet.create(id=pk, user=user, content=tweet)\n\n    def _test_query(self, alias_expr):\n        UA = alias_expr()\n        return (Tweet\n                .select(Tweet, UA)\n                .order_by(UA.username, Tweet.content))\n\n    def assertTweets(self, query, user_attr='user'):\n        with self.assertQueryCount(1):\n            data = [(getattr(tweet, user_attr).username, tweet.content)\n                    for tweet in query]\n        self.assertEqual(sorted(self.data), data)\n\n    def test_control(self):\n        self.assertTweets(self._test_query(lambda: User).join(User))\n\n    def test_join_aliased_columns(self):\n        query = (Tweet\n                 .select(Tweet.id.alias('tweet_id'), Tweet.content)\n                 .order_by(Tweet.id))\n        self.assertEqual([(t.tweet_id, t.content) for t in query], [\n            (1, 'meow'),\n            (2, 'purr'),\n            (3, 'hiss'),\n            (4, 'woof')])\n\n        query = (Tweet\n                 .select(Tweet.id.alias('tweet_id'), Tweet.content)\n                 .join(User)\n                 .where(User.username == 'huey')\n                 .order_by(Tweet.id))\n        self.assertEqual([(t.tweet_id, t.content) for t in query], [\n            (1, 'meow'),\n            (2, 'purr')])\n\n    def test_join(self):\n        UA = User.alias('ua')\n        query = self._test_query(lambda: UA).join(UA)\n        self.assertTweets(query)\n\n    def test_join_on(self):\n        UA = User.alias('ua')\n        query = self._test_query(lambda: UA).join(UA, on=(Tweet.user == UA.id))\n        self.assertTweets(query)\n\n    def test_join_on_field(self):\n        UA = User.alias('ua')\n        query = self._test_query(lambda: UA)\n        query = query.join(UA, on=Tweet.user)\n        self.assertTweets(query)\n\n    def test_join_on_alias(self):\n        UA = User.alias('ua')\n        query = self._test_query(lambda: UA)\n        query = query.join(UA, on=(Tweet.user == UA.id).alias('foo'))\n        self.assertTweets(query, 'foo')\n\n    def test_join_attr(self):\n        UA = User.alias('ua')\n        query = self._test_query(lambda: UA).join(UA, attr='baz')\n        self.assertTweets(query, 'baz')\n\n    def test_join_on_alias_attr(self):\n        UA = User.alias('ua')\n        q = self._test_query(lambda: UA)\n        q = q.join(UA, on=(Tweet.user == UA.id).alias('foo'), attr='bar')\n        self.assertTweets(q, 'bar')\n\n    def _test_query_backref(self, alias_expr):\n        TA = alias_expr()\n        return (User\n                .select(User, TA)\n                .order_by(User.username, TA.content))\n\n    def assertUsers(self, query, tweet_attr='tweet'):\n        with self.assertQueryCount(1):\n            data = [(user.username, getattr(user, tweet_attr).content)\n                    for user in query]\n        self.assertEqual(sorted(self.data), data)\n\n    def test_control_backref(self):\n        self.assertUsers(self._test_query_backref(lambda: Tweet).join(Tweet))\n\n    def test_join_backref(self):\n        TA = Tweet.alias('ta')\n        query = self._test_query_backref(lambda: TA).join(TA)\n        self.assertUsers(query)\n\n    def test_join_on_backref(self):\n        TA = Tweet.alias('ta')\n        query = self._test_query_backref(lambda: TA)\n        query = query.join(TA, on=(User.id == TA.user_id))\n        self.assertUsers(query)\n\n    def test_join_on_field_backref(self):\n        TA = Tweet.alias('ta')\n        query = self._test_query_backref(lambda: TA)\n        query = query.join(TA, on=TA.user)\n        self.assertUsers(query)\n\n    def test_join_on_alias_backref(self):\n        TA = Tweet.alias('ta')\n        query = self._test_query_backref(lambda: TA)\n        query = query.join(TA, on=(User.id == TA.user_id).alias('foo'))\n        self.assertUsers(query, 'foo')\n\n    def test_join_attr_backref(self):\n        TA = Tweet.alias('ta')\n        query = self._test_query_backref(lambda: TA).join(TA, attr='baz')\n        self.assertUsers(query, 'baz')\n\n    def test_join_alias_twice(self):\n        # Test that a model-alias can be both the source and the dest by\n        # joining from User -> Tweet -> User (as \"foo\").\n        TA = Tweet.alias('ta')\n        UA = User.alias('ua')\n        with self.assertQueryCount(1):\n            query = (User\n                     .select(User, TA, UA)\n                     .join(TA)\n                     .join(UA, on=(TA.user_id == UA.id).alias('foo'))\n                     .order_by(User.username, TA.content))\n\n            data = [(row.username, row.tweet.content, row.tweet.foo.username)\n                    for row in query]\n\n        self.assertEqual(data, [\n            ('huey', 'meow', 'huey'),\n            ('huey', 'purr', 'huey'),\n            ('mickey', 'woof', 'mickey'),\n            ('zaizee', 'hiss', 'zaizee')])\n\n    def test_alias_filter(self):\n        UA = User.alias('ua')\n        lookups = ({'ua__username': 'huey'}, {'user__username': 'huey'})\n        for lookup in lookups:\n            with self.assertQueryCount(1):\n                query = (Tweet\n                         .select(Tweet.content, UA.username)\n                         .join(UA)\n                         .filter(**lookup)\n                         .order_by(Tweet.content))\n\n                self.assertSQL(query, (\n                    'SELECT \"t1\".\"content\", \"ua\".\"username\" '\n                    'FROM \"tweet\" AS \"t1\" '\n                    'INNER JOIN \"users\" AS \"ua\" '\n                    'ON (\"t1\".\"user_id\" = \"ua\".\"id\") '\n                    'WHERE (\"ua\".\"username\" = ?) '\n                    'ORDER BY \"t1\".\"content\"'), ['huey'])\n\n                data = [(t.content, t.user.username) for t in query]\n                self.assertEqual(data, [('meow', 'huey'), ('purr', 'huey')])\n\n\n@skip_unless(\n    IS_POSTGRESQL or IS_MYSQL_ADVANCED_FEATURES or IS_SQLITE_25 or IS_CRDB,\n    'window function')\nclass TestWindowFunctionIntegration(ModelTestCase):\n    requires = [Sample]\n\n    def setUp(self):\n        super(TestWindowFunctionIntegration, self).setUp()\n        values = ((1, 10), (1, 20), (2, 1), (2, 3), (3, 100))\n        with self.database.atomic():\n            for counter, value in values:\n                Sample.create(counter=counter, value=value)\n\n    def test_simple_partition(self):\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.AVG(Sample.value).over(\n                             partition_by=[Sample.counter]))\n                 .order_by(Sample.counter, Sample.value)\n                 .tuples())\n        expected = [\n            (1, 10., 15.),\n            (1, 20., 15.),\n            (2, 1., 2.),\n            (2, 3., 2.),\n            (3, 100., 100.)]\n        self.assertEqual(list(query), expected)\n\n        window = Window(partition_by=[Sample.counter])\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.AVG(Sample.value).over(window))\n                 .window(window)\n                 .order_by(Sample.counter, Sample.value)\n                 .tuples())\n        self.assertEqual(list(query), expected)\n\n    def test_mixed_ordering(self):\n        s = fn.SUM(Sample.value).over(order_by=[Sample.value])\n        query = (Sample\n                 .select(Sample.counter, Sample.value, s.alias('rtotal'))\n                 .order_by(Sample.id))\n        # We end up with window going 1., 3., 10., 20., 100..\n        # So:\n        # 1 |  10 | (1 + 3 + 10)\n        # 1 |  20 | (1 + 3 + 10  + 20)\n        # 2 |   1 | (1)\n        # 2 |   3 | (1 + 3)\n        # 3 | 100 | (1 + 3 + 10 + 20 + 100)\n        self.assertEqual([(r.counter, r.value, r.rtotal) for r in query], [\n            (1, 10., 14.),\n            (1, 20., 34.),\n            (2, 1., 1.),\n            (2, 3., 4.),\n            (3, 100., 134.)])\n\n    def test_reuse_window(self):\n        w = Window(order_by=[Sample.value])\n        with self.database.atomic():\n            Sample.delete().execute()\n            for i in range(10):\n                Sample.create(counter=i, value=10 * i)\n\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.NTILE(4).over(w).alias('quartile'),\n                         fn.NTILE(5).over(w).alias('quintile'),\n                         fn.NTILE(100).over(w).alias('percentile'))\n                 .window(w)\n                 .order_by(Sample.id))\n        results = [(r.counter, r.value, r.quartile, r.quintile, r.percentile)\n                   for r in query]\n        self.assertEqual(results, [\n            # ct, v, 4tile, 5tile, 100tile\n            (0, 0., 1, 1, 1),\n            (1, 10., 1, 1, 2),\n            (2, 20., 1, 2, 3),\n            (3, 30., 2, 2, 4),\n            (4, 40., 2, 3, 5),\n            (5, 50., 2, 3, 6),\n            (6, 60., 3, 4, 7),\n            (7, 70., 3, 4, 8),\n            (8, 80., 4, 5, 9),\n            (9, 90., 4, 5, 10),\n        ])\n\n    def test_ordered_window(self):\n        window = Window(partition_by=[Sample.counter],\n                        order_by=[Sample.value.desc()])\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.RANK().over(window=window).alias('rank'))\n                 .window(window)\n                 .order_by(Sample.counter, fn.RANK().over(window=window))\n                 .tuples())\n        self.assertEqual(list(query), [\n            (1, 20., 1),\n            (1, 10., 2),\n            (2, 3., 1),\n            (2, 1., 2),\n            (3, 100., 1)])\n\n    def test_two_windows(self):\n        w1 = Window(partition_by=[Sample.counter]).alias('w1')\n        w2 = Window(order_by=[Sample.counter]).alias('w2')\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.AVG(Sample.value).over(window=w1),\n                         fn.RANK().over(window=w2))\n                 .window(w1, w2)\n                 .order_by(Sample.id)\n                 .tuples())\n        self.assertEqual(list(query), [\n            (1, 10., 15., 1),\n            (1, 20., 15., 1),\n            (2, 1., 2., 3),\n            (2, 3., 2., 3),\n            (3, 100., 100., 5)])\n\n    def test_empty_over(self):\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.LAG(Sample.counter, 1).over(order_by=[Sample.id]))\n                 .order_by(Sample.id)\n                 .tuples())\n        self.assertEqual(list(query), [\n            (1, 10., None),\n            (1, 20., 1),\n            (2, 1., 1),\n            (2, 3., 2),\n            (3, 100., 2)])\n\n    def test_bounds(self):\n        query = (Sample\n                 .select(Sample.value,\n                         fn.SUM(Sample.value).over(\n                             partition_by=[Sample.counter],\n                             start=Window.preceding(),\n                             end=Window.following(1)))\n                 .order_by(Sample.id)\n                 .tuples())\n        self.assertEqual(list(query), [\n            (10., 30.),\n            (20., 30.),\n            (1., 4.),\n            (3., 4.),\n            (100., 100.)])\n\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.SUM(Sample.value).over(\n                             order_by=[Sample.id],\n                             start=Window.preceding(2)))\n                 .order_by(Sample.id)\n                 .tuples())\n        self.assertEqual(list(query), [\n            (1, 10., 10.),\n            (1, 20., 30.),\n            (2, 1., 31.),\n            (2, 3., 24.),\n            (3, 100., 104.)])\n\n    def test_frame_types(self):\n        Sample.create(counter=1, value=20.)\n        Sample.create(counter=2, value=1.)  # Observe logical peer handling.\n\n        # Defaults to RANGE.\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.SUM(Sample.value).over(\n                             order_by=[Sample.counter, Sample.value]))\n                 .order_by(Sample.id))\n        self.assertEqual(list(query.tuples()), [\n            (1, 10., 10.),\n            (1, 20., 50.),\n            (2, 1., 52.),\n            (2, 3., 55.),\n            (3, 100., 155.),\n            (1, 20., 50.),\n            (2, 1., 52.)])\n\n        # Explicitly specify ROWS.\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.SUM(Sample.value).over(\n                             order_by=[Sample.counter, Sample.value],\n                             frame_type=Window.ROWS))\n                 .order_by(Sample.counter, Sample.value))\n        self.assertEqual(list(query.tuples()), [\n            (1, 10., 10.),\n            (1, 20., 30.),\n            (1, 20., 50.),\n            (2, 1., 51.),\n            (2, 1., 52.),\n            (2, 3., 55.),\n            (3, 100., 155.)])\n\n        # Including a boundary results in ROWS.\n        query = (Sample\n                 .select(Sample.counter, Sample.value,\n                         fn.SUM(Sample.value).over(\n                             order_by=[Sample.counter, Sample.value],\n                             start=Window.preceding(2)))\n                 .order_by(Sample.counter, Sample.value))\n        self.assertEqual(list(query.tuples()), [\n            (1, 10., 10.),\n            (1, 20., 30.),\n            (1, 20., 50.),\n            (2, 1., 41.),\n            (2, 1., 22.),\n            (2, 3., 5.),\n            (3, 100., 104.)])\n\n    @skip_if(IS_MYSQL, 'requires OVER() with FILTER')\n    def test_filter_clause(self):\n        condsum = fn.SUM(Sample.value).filter(Sample.counter > 1).over(\n            order_by=[Sample.id], start=Window.preceding(1))\n        query = (Sample\n                 .select(Sample.counter, Sample.value, condsum.alias('cs'))\n                 .order_by(Sample.value))\n        self.assertEqual(list(query.tuples()), [\n            (2, 1., 1.),\n            (2, 3., 4.),\n            (1, 10., None),\n            (1, 20., None),\n            (3, 100., 103.),\n        ])\n\n    @skip_if(IS_MYSQL or (IS_SQLITE and not IS_SQLITE_30),\n             'requires FILTER with aggregates')\n    def test_filter_with_aggregate(self):\n        condsum = fn.SUM(Sample.value).filter(Sample.counter > 1)\n        query = (Sample\n                 .select(Sample.counter, condsum.alias('cs'))\n                 .group_by(Sample.counter)\n                 .order_by(Sample.counter))\n        self.assertEqual(list(query.tuples()), [\n            (1, None),\n            (2, 4.),\n            (3, 100.)])\n\n    def test_row_number(self):\n        query = (Sample\n                 .select(Sample.counter,\n                         fn.ROW_NUMBER().over(\n                             order_by=[Sample.counter]).alias('rn'))\n                 .order_by(Sample.counter)\n                 .tuples())\n        self.assertEqual(list(query),\n                         [(1, 1), (1, 2), (2, 3), (2, 4), (3, 5)])\n\n    def test_sum_with_frame(self):\n        w = Window(order_by=[Sample.counter],\n                   frame_type=Window.ROWS,\n                   start=Window.preceding(1),\n                   end=Window.CURRENT_ROW)\n        query = (Sample\n                 .select(Sample.counter,\n                         fn.SUM(Sample.value).over(w).alias('rsum'))\n                 .window(w)\n                 .order_by(Sample.counter)\n                 .tuples())\n        results = list(query)\n        # Each row sums current + previous row's value.\n        self.assertEqual(results, [\n            (1, 10.0),  # just 10\n            (1, 30.0),  # 10 + 20\n            (2, 21.0),  # 20 + 1\n            (2, 4.0),  # 1 + 3\n            (3, 103.0)])  # 3 + 100\n\n    def test_lag_lead(self):\n        query = (Sample\n                 .select(Sample.counter,\n                         fn.LAG(Sample.value, 1).over(\n                             order_by=[Sample.counter]).alias('prev'),\n                         fn.LEAD(Sample.value, 1).over(\n                             order_by=[Sample.counter]).alias('next'))\n                 .order_by(Sample.counter)\n                 .tuples())\n        results = list(query)\n        self.assertEqual(results, [\n            (1, None, 20.0),\n            (1, 10.0, 1.0),\n            (2, 20.0, 3.0),\n            (2, 1.0, 100.0),\n            (3, 3.0, None)])\n        #values = ((1, 10), (1, 20), (2, 1), (2, 3), (3, 100))\n\n\n@skip_if(IS_SQLITE or (IS_MYSQL and not IS_MYSQL_ADVANCED_FEATURES))\n@skip_unless(db.for_update, 'requires for update')\nclass TestForUpdateIntegration(ModelTestCase):\n    requires = [User, Tweet]\n\n    def setUp(self):\n        super(TestForUpdateIntegration, self).setUp()\n        self.alt_db = new_connection()\n        class AltUser(User):\n            class Meta:\n                database = self.alt_db\n                table_name = User._meta.table_name\n        class AltTweet(Tweet):\n            class Meta:\n                database = self.alt_db\n                table_name = Tweet._meta.table_name\n        self.AltUser = AltUser\n        self.AltTweet = AltTweet\n\n    def tearDown(self):\n        self.alt_db.close()\n        super(TestForUpdateIntegration, self).tearDown()\n\n    @skip_if(IS_CRDB, 'crdb locks-up on this test, blocking reads')\n    def test_for_update(self):\n        with self.database.atomic():\n            User.create(username='huey')\n            zaizee = User.create(username='zaizee')\n\n        AltUser = self.AltUser\n\n        with self.database.manual_commit():\n            self.database.begin()\n            users = (User.select().where(User.username == 'zaizee')\n                     .for_update()\n                     .execute())\n            updated = (User\n                       .update(username='ziggy')\n                       .where(User.username == 'zaizee')\n                       .execute())\n            self.assertEqual(updated, 1)\n\n            if IS_POSTGRESQL:\n                nrows = (AltUser\n                         .update(username='huey-x')\n                         .where(AltUser.username == 'huey')\n                         .execute())\n                self.assertEqual(nrows, 1)\n\n            query = (AltUser\n                     .select(AltUser.username)\n                     .where(AltUser.id == zaizee.id))\n            self.assertEqual(query.get().username, 'zaizee')\n\n            self.database.commit()\n            self.assertEqual(query.get().username, 'ziggy')\n\n    def test_for_update_blocking(self):\n        User.create(username='u1')\n\n        AltUser = self.AltUser\n        evt = threading.Event()\n        def run_in_thread():\n            with self.alt_db.atomic():\n                evt.wait()\n                n = (AltUser.update(username='u1-y')\n                     .where(AltUser.username == 'u1')\n                     .execute())\n                self.assertEqual(n, 0)\n\n        t = threading.Thread(target=run_in_thread)\n        t.daemon = True\n        t.start()\n\n        with self.database.atomic() as txn:\n            q = (User.select()\n                 .where(User.username == 'u1')\n                 .for_update()\n                 .execute())\n            evt.set()\n\n            n = (User.update(username='u1-x')\n                 .where(User.username == 'u1')\n                 .execute())\n            self.assertEqual(n, 1)\n\n        t.join(timeout=5)\n        u = User.get()\n        self.assertEqual(u.username, 'u1-x')\n\n    def test_for_update_nested(self):\n        User.insert_many([(u,) for u in 'abc']).execute()\n        subq = User.select().where(User.username != 'b').for_update()\n        nrows = (User\n                 .delete()\n                 .where(User.id.in_(subq))\n                 .execute())\n        self.assertEqual(nrows, 2)\n\n    def test_for_update_nowait(self):\n        User.create(username='huey')\n        zaizee = User.create(username='zaizee')\n\n        AltUser = self.AltUser\n\n        with self.database.manual_commit():\n            self.database.begin()\n            users = (User\n                     .select(User.username)\n                     .where(User.username == 'zaizee')\n                     .for_update(nowait=True)\n                     .execute())\n\n            def will_fail():\n                return (AltUser\n                        .select()\n                        .where(AltUser.username == 'zaizee')\n                        .for_update(nowait=True)\n                        .get())\n\n            self.assertRaises((OperationalError, InternalError), will_fail)\n            self.database.commit()\n\n    @requires_postgresql\n    @requires_models(User, Tweet)\n    def test_for_update_of(self):\n        h = User.create(username='huey')\n        z = User.create(username='zaizee')\n        Tweet.create(user=h, content='h')\n        Tweet.create(user=z, content='z')\n\n        AltUser, AltTweet = self.AltUser, self.AltTweet\n\n        with self.database.manual_commit():\n            self.database.begin()\n            # Lock tweets by huey.\n            query = (Tweet\n                     .select()\n                     .join(User)\n                     .where(User.username == 'huey')\n                     .for_update(of=Tweet, nowait=True))\n            qr = query.execute()\n\n            # No problem updating zaizee's tweet or huey's user.\n            nrows = (AltTweet\n                     .update(content='zx')\n                     .where(AltTweet.user == z.id)\n                     .execute())\n            self.assertEqual(nrows, 1)\n\n            nrows = (AltUser\n                     .update(username='huey-x')\n                     .where(AltUser.username == 'huey')\n                     .execute())\n            self.assertEqual(nrows, 1)\n\n            def will_fail():\n                (AltTweet\n                 .select()\n                 .where(AltTweet.user == h)\n                 .for_update(nowait=True)\n                 .get())\n            self.assertRaises((OperationalError, InternalError), will_fail)\n\n            self.database.commit()\n\n        query = Tweet.select(Tweet, User).join(User).order_by(Tweet.id)\n        self.assertEqual([(t.content, t.user.username) for t in query],\n                         [('h', 'huey-x'), ('zx', 'zaizee')])\n\n\nclass ServerDefault(TestModel):\n    timestamp = DateTimeField(constraints=[SQL('default (now())')])\n\n\n@requires_postgresql\nclass TestReturningIntegration(ModelTestCase):\n    requires = [User]\n\n    def test_simple_returning(self):\n        query = User.insert(username='charlie')\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"username\") VALUES (?) '\n            'RETURNING \"users\".\"id\"'),\n            ['charlie'])\n\n        self.assertEqual(query.execute(), 1)\n\n        # By default returns a tuple.\n        query = User.insert(username='huey')\n        self.assertEqual(query.execute(), 2)\n        self.assertEqual(list(query), [(2,)])\n\n        # If we specify a returning clause we get user instances.\n        query = User.insert(username='snoobie').returning(User)\n        query.execute()\n        self.assertEqual([x.username for x in query], ['snoobie'])\n\n        query = (User\n                 .insert(username='zaizee')\n                 .returning(User.id, User.username)\n                 .dicts())\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"username\") VALUES (?) '\n            'RETURNING \"users\".\"id\", \"users\".\"username\"'), ['zaizee'])\n\n        cursor = query.execute()\n        row, = list(cursor)\n        self.assertEqual(row, {'id': 4, 'username': 'zaizee'})\n\n        query = (User\n                 .insert(username='mickey')\n                 .returning(User)\n                 .objects())\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"username\") VALUES (?) '\n            'RETURNING \"users\".\"id\", \"users\".\"username\"'), ['mickey'])\n        cursor = query.execute()\n        row, = list(cursor)\n        self.assertEqual(row.id, 5)\n        self.assertEqual(row.username, 'mickey')\n\n        # Can specify aliases.\n        query = (User\n                 .insert(username='sipp')\n                 .returning(User.username.alias('new_username')))\n        self.assertEqual([x.new_username for x in query.execute()], ['sipp'])\n\n        # Minimal test with insert_many.\n        query = User.insert_many([('u7',), ('u8',)])\n        self.assertEqual([r for r, in query.execute()], [7, 8])\n\n        # Test with insert / on conflict.\n        query = (User\n                 .insert_many([(7, 'u7',), (9, 'u9',)],\n                              [User.id, User.username])\n                 .on_conflict(conflict_target=[User.id],\n                              update={User.username: User.username + 'x'})\n                 .returning(User))\n        self.assertEqual([(x.id, x.username) for x in query],\n                         [(7, 'u7x'), (9, 'u9')])\n\n    def test_simple_returning_insert_update_delete(self):\n        res = User.insert(username='charlie').returning(User).execute()\n        self.assertEqual([u.username for u in res], ['charlie'])\n\n        res = (User\n               .update(username='charlie2')\n               .where(User.id == 1)\n               .returning(User)\n               .execute())\n        # Subsequent iterations are cached.\n        for _ in range(2):\n            self.assertEqual([u.username for u in res], ['charlie2'])\n\n        res = (User\n               .delete()\n               .where(User.id == 1)\n               .returning(User)\n               .execute())\n        # Subsequent iterations are cached.\n        for _ in range(2):\n            self.assertEqual([u.username for u in res], ['charlie2'])\n\n    def test_simple_insert_update_delete_no_returning(self):\n        query = User.insert(username='charlie')\n        self.assertEqual(query.execute(), 1)\n\n        query = User.insert(username='huey')\n        self.assertEqual(query.execute(), 2)\n\n        query = User.update(username='huey2').where(User.username == 'huey')\n        self.assertEqual(query.execute(), 1)\n        self.assertEqual(query.execute(), 0)  # No rows updated!\n\n        query = User.delete().where(User.username == 'huey2')\n        self.assertEqual(query.execute(), 1)\n        self.assertEqual(query.execute(), 0)  # No rows updated!\n\n    @requires_models(ServerDefault)\n    def test_returning_server_defaults(self):\n        query = (ServerDefault\n                 .insert()\n                 .returning(ServerDefault.id, ServerDefault.timestamp))\n        self.assertSQL(query, (\n            'INSERT INTO \"server_default\" '\n            'DEFAULT VALUES '\n            'RETURNING \"server_default\".\"id\", \"server_default\".\"timestamp\"'),\n            [])\n\n        with self.assertQueryCount(1):\n            cursor = query.dicts().execute()\n            row, = list(cursor)\n\n        self.assertTrue(row['timestamp'] is not None)\n\n        obj = ServerDefault.get(ServerDefault.id == row['id'])\n        self.assertEqual(obj.timestamp, row['timestamp'])\n\n    def test_no_return(self):\n        query = User.insert(username='huey').returning()\n        self.assertIsNone(query.execute())\n\n        user = User.get(User.username == 'huey')\n        self.assertEqual(user.username, 'huey')\n        self.assertTrue(user.id >= 1)\n\n    @requires_models(Category)\n    def test_non_int_pk_returning(self):\n        query = Category.insert(name='root')\n        self.assertSQL(query, (\n            'INSERT INTO \"category\" (\"name\") VALUES (?) '\n            'RETURNING \"category\".\"name\"'), ['root'])\n\n        self.assertEqual(query.execute(), 'root')\n\n    def test_returning_multi(self):\n        data = [{'username': 'huey'}, {'username': 'mickey'}]\n        query = User.insert_many(data)\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"username\") VALUES (?), (?) '\n            'RETURNING \"users\".\"id\"'), ['huey', 'mickey'])\n\n        data = query.execute()\n\n        # Check that the result wrapper is correctly set up.\n        self.assertTrue(len(data.select) == 1 and data.select[0] is User.id)\n        self.assertEqual(list(data), [(1,), (2,)])\n\n        query = (User\n                 .insert_many([{'username': 'foo'},\n                               {'username': 'bar'},\n                               {'username': 'baz'}])\n                 .returning(User.id, User.username)\n                 .namedtuples())\n        data = query.execute()\n        self.assertEqual([(row.id, row.username) for row in data], [\n            (3, 'foo'),\n            (4, 'bar'),\n            (5, 'baz')])\n\n    @requires_models(Category)\n    def test_returning_query(self):\n        for name in ('huey', 'mickey', 'zaizee'):\n            Category.create(name=name)\n\n        source = Category.select(Category.name).order_by(Category.name)\n        query = User.insert_from(source, (User.username,))\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"username\") '\n            'SELECT \"t1\".\"name\" FROM \"category\" AS \"t1\" ORDER BY \"t1\".\"name\" '\n            'RETURNING \"users\".\"id\"'), [])\n\n        data = query.execute()\n\n        # Check that the result wrapper is correctly set up.\n        self.assertTrue(len(data.select) == 1 and data.select[0] is User.id)\n        self.assertEqual(list(data), [(1,), (2,), (3,)])\n\n    def test_update_returning(self):\n        id_list = User.insert_many([{'username': 'huey'},\n                                    {'username': 'zaizee'}]).execute()\n        huey_id, zaizee_id = [pk for pk, in id_list]\n\n        query = (User\n                 .update(username='ziggy')\n                 .where(User.username == 'zaizee')\n                 .returning(User.id, User.username))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"username\" = ? '\n            'WHERE (\"users\".\"username\" = ?) '\n            'RETURNING \"users\".\"id\", \"users\".\"username\"'), ['ziggy', 'zaizee'])\n        data = query.execute()\n        user = data[0]\n        self.assertEqual(user.username, 'ziggy')\n        self.assertEqual(user.id, zaizee_id)\n\n    def test_delete_returning(self):\n        id_list = User.insert_many([{'username': 'huey'},\n                                    {'username': 'zaizee'}]).execute()\n        huey_id, zaizee_id = [pk for pk, in id_list]\n\n        query = (User\n                 .delete()\n                 .where(User.username == 'zaizee')\n                 .returning(User.id, User.username))\n        self.assertSQL(query, (\n            'DELETE FROM \"users\" WHERE (\"users\".\"username\" = ?) '\n            'RETURNING \"users\".\"id\", \"users\".\"username\"'), ['zaizee'])\n        data = query.execute()\n        user = data[0]\n        self.assertEqual(user.username, 'zaizee')\n        self.assertEqual(user.id, zaizee_id)\n\n\nclass Member(TestModel):\n    name = TextField()\n    recommendedby = ForeignKeyField('self', null=True)\n\n\nclass TestCTEIntegration(ModelTestCase):\n    requires = [Category]\n\n    def setUp(self):\n        super(TestCTEIntegration, self).setUp()\n        CC = Category.create\n        root = CC(name='root')\n        p1 = CC(name='p1', parent=root)\n        p2 = CC(name='p2', parent=root)\n        p3 = CC(name='p3', parent=root)\n        c11 = CC(name='c11', parent=p1)\n        c12 = CC(name='c12', parent=p1)\n        c31 = CC(name='c31', parent=p3)\n\n    @skip_if(IS_SQLITE_OLD or (IS_MYSQL and not IS_MYSQL_ADVANCED_FEATURES)\n             or IS_CRDB)\n    @requires_models(Member)\n    def test_docs_example(self):\n        f = Member.create(name='founder')\n        gen2_1 = Member.create(name='g2-1', recommendedby=f)\n        gen2_2 = Member.create(name='g2-2', recommendedby=f)\n        gen2_3 = Member.create(name='g2-3', recommendedby=f)\n        gen3_1_1 = Member.create(name='g3-1-1', recommendedby=gen2_1)\n        gen3_1_2 = Member.create(name='g3-1-2', recommendedby=gen2_1)\n        gen3_3_1 = Member.create(name='g3-3-1', recommendedby=gen2_3)\n\n        # Get recommender chain for 331.\n        base = (Member\n                .select(Member.recommendedby)\n                .where(Member.id == gen3_3_1.id)\n                .cte('recommenders', recursive=True, columns=('recommender',)))\n\n        MA = Member.alias()\n        recursive = (MA\n                     .select(MA.recommendedby)\n                     .join(base, on=(MA.id == base.c.recommender)))\n\n        cte = base.union_all(recursive)\n        query = (cte\n                 .select_from(cte.c.recommender, Member.name)\n                 .join(Member, on=(cte.c.recommender == Member.id))\n                 .order_by(Member.id.desc()))\n        self.assertEqual([m.name for m in query], ['g2-3', 'founder'])\n\n    @skip_if(IS_SQLITE_OLD or (IS_MYSQL and not IS_MYSQL_ADVANCED_FEATURES))\n    def test_simple_cte(self):\n        cte = (Category\n               .select(Category.name, Category.parent)\n               .cte('catz', columns=('name', 'parent')))\n\n        cte_sql = ('WITH \"catz\" (\"name\", \"parent\") AS ('\n                   'SELECT \"t1\".\"name\", \"t1\".\"parent_id\" '\n                   'FROM \"category\" AS \"t1\") '\n                   'SELECT \"catz\".\"name\", \"catz\".\"parent\" AS \"pname\" '\n                   'FROM \"catz\" '\n                   'ORDER BY \"catz\".\"name\"')\n\n        query = (Category\n                 .select(cte.c.name, cte.c.parent.alias('pname'))\n                 .from_(cte)\n                 .order_by(cte.c.name)\n                 .with_cte(cte))\n        self.assertSQL(query, cte_sql, [])\n\n        query2 = (cte.select_from(cte.c.name, cte.c.parent.alias('pname'))\n                  .order_by(cte.c.name))\n        self.assertSQL(query2, cte_sql, [])\n\n        self.assertEqual([(row.name, row.pname) for row in query], [\n            ('c11', 'p1'),\n            ('c12', 'p1'),\n            ('c31', 'p3'),\n            ('p1', 'root'),\n            ('p2', 'root'),\n            ('p3', 'root'),\n            ('root', None)])\n        self.assertEqual([(row.name, row.pname) for row in query],\n                         [(row.name, row.pname) for row in query2])\n\n    @skip_if(IS_SQLITE_OLD or (IS_MYSQL and not IS_MYSQL_ADVANCED_FEATURES))\n    def test_cte_join(self):\n        cte = (Category\n               .select(Category.name)\n               .cte('parents', columns=('name',)))\n\n        query = (Category\n                 .select(Category.name, cte.c.name.alias('pname'))\n                 .join(cte, on=(Category.parent == cte.c.name))\n                 .order_by(Category.name)\n                 .with_cte(cte))\n\n        self.assertSQL(query, (\n            'WITH \"parents\" (\"name\") AS ('\n            'SELECT \"t1\".\"name\" FROM \"category\" AS \"t1\") '\n            'SELECT \"t2\".\"name\", \"parents\".\"name\" AS \"pname\" '\n            'FROM \"category\" AS \"t2\" '\n            'INNER JOIN \"parents\" ON (\"t2\".\"parent_id\" = \"parents\".\"name\") '\n            'ORDER BY \"t2\".\"name\"'), [])\n        self.assertEqual([(c.name, c.parents['pname']) for c in query], [\n            ('c11', 'p1'),\n            ('c12', 'p1'),\n            ('c31', 'p3'),\n            ('p1', 'root'),\n            ('p2', 'root'),\n            ('p3', 'root'),\n        ])\n\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL or IS_CRDB, 'requires recursive cte')\n    def test_recursive_cte(self):\n        def get_parents(cname):\n            C1 = Category.alias()\n            C2 = Category.alias()\n\n            level = SQL('1').cast('integer').alias('level')\n            path = C1.name.cast('text').alias('path')\n\n            base = (C1\n                    .select(C1.name, C1.parent, level, path)\n                    .where(C1.name == cname)\n                    .cte('parents', recursive=True))\n\n            rlevel = (base.c.level + 1).alias('level')\n            rpath = base.c.path.concat('->').concat(C2.name).alias('path')\n            recursive = (C2\n                         .select(C2.name, C2.parent, rlevel, rpath)\n                         .from_(base)\n                         .join(C2, on=(C2.name == base.c.parent_id)))\n\n            cte = base + recursive\n            query = (cte\n                     .select_from(cte.c.name, cte.c.level, cte.c.path)\n                     .order_by(cte.c.level))\n            self.assertSQL(query, (\n                'WITH RECURSIVE \"parents\" AS ('\n                'SELECT \"t1\".\"name\", \"t1\".\"parent_id\", '\n                'CAST(1 AS integer) AS \"level\", '\n                'CAST(\"t1\".\"name\" AS text) AS \"path\" '\n                'FROM \"category\" AS \"t1\" '\n                'WHERE (\"t1\".\"name\" = ?) '\n                'UNION ALL '\n                'SELECT \"t2\".\"name\", \"t2\".\"parent_id\", '\n                '(\"parents\".\"level\" + ?) AS \"level\", '\n                '((\"parents\".\"path\" || ?) || \"t2\".\"name\") AS \"path\" '\n                'FROM \"parents\" '\n                'INNER JOIN \"category\" AS \"t2\" '\n                'ON (\"t2\".\"name\" = \"parents\".\"parent_id\")) '\n                'SELECT \"parents\".\"name\", \"parents\".\"level\", \"parents\".\"path\" '\n                'FROM \"parents\" '\n                'ORDER BY \"parents\".\"level\"'), [cname, 1, '->'])\n            return query\n\n        data = [row for row in get_parents('c31').tuples()]\n        self.assertEqual(data, [\n            ('c31', 1, 'c31'),\n            ('p3', 2, 'c31->p3'),\n            ('root', 3, 'c31->p3->root')])\n\n        data = [(c.name, c.level, c.path)\n                for c in get_parents('c12').namedtuples()]\n        self.assertEqual(data, [\n            ('c12', 1, 'c12'),\n            ('p1', 2, 'c12->p1'),\n            ('root', 3, 'c12->p1->root')])\n\n        query = get_parents('root')\n        data = [(r.name, r.level, r.path) for r in query]\n        self.assertEqual(data, [('root', 1, 'root')])\n\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL or IS_CRDB, 'requires recursive cte')\n    def test_recursive_cte2(self):\n        hierarchy = (Category\n                     .select(Category.name, Value(0).alias('level'))\n                     .where(Category.parent.is_null(True))\n                     .cte(name='hierarchy', recursive=True))\n\n        C = Category.alias()\n        recursive = (C\n                     .select(C.name, (hierarchy.c.level + 1).alias('level'))\n                     .join(hierarchy, on=(C.parent == hierarchy.c.name)))\n\n        cte = hierarchy.union_all(recursive)\n        query = (cte\n                 .select_from(cte.c.name, cte.c.level)\n                 .order_by(cte.c.name))\n        self.assertEqual([(r.name, r.level) for r in query], [\n            ('c11', 2),\n            ('c12', 2),\n            ('c31', 2),\n            ('p1', 1),\n            ('p2', 1),\n            ('p3', 1),\n            ('root', 0)])\n\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL or IS_CRDB, 'requires recursive cte')\n    def test_recursive_cte_docs_example(self):\n        # Define the base case of our recursive CTE. This will be categories that\n        # have a null parent foreign-key.\n        Base = Category.alias()\n        level = Value(1).cast('integer').alias('level')\n        path = Base.name.cast('text').alias('path')\n        base_case = (Base\n                     .select(Base.name, Base.parent, level, path)\n                     .where(Base.parent.is_null())\n                     .cte('base', recursive=True))\n\n        # Define the recursive terms.\n        RTerm = Category.alias()\n        rlevel = (base_case.c.level + 1).alias('level')\n        rpath = base_case.c.path.concat('->').concat(RTerm.name).alias('path')\n        recursive = (RTerm\n                     .select(RTerm.name, RTerm.parent, rlevel, rpath)\n                     .join(base_case, on=(RTerm.parent == base_case.c.name)))\n\n        # The recursive CTE is created by taking the base case and UNION ALL with\n        # the recursive term.\n        cte = base_case.union_all(recursive)\n\n        # We will now query from the CTE to get the categories, their levels,  and\n        # their paths.\n        query = (cte\n                 .select_from(cte.c.name, cte.c.level, cte.c.path)\n                 .order_by(cte.c.path))\n        data = [(obj.name, obj.level, obj.path) for obj in query]\n        self.assertEqual(data, [\n            ('root', 1, 'root'),\n            ('p1', 2, 'root->p1'),\n            ('c11', 3, 'root->p1->c11'),\n            ('c12', 3, 'root->p1->c12'),\n            ('p2', 2, 'root->p2'),\n            ('p3', 2, 'root->p3'),\n            ('c31', 3, 'root->p3->c31')])\n\n    @requires_models(Sample)\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL, 'sqlite too old for ctes, mysql flaky')\n    def test_cte_reuse_aggregate(self):\n        data = (\n            (1, (1.25, 1.5, 1.75)),\n            (2, (2.1, 2.3, 2.5, 2.7, 2.9)),\n            (3, (3.5, 3.5)))\n        with self.database.atomic():\n            for counter, values in data:\n                (Sample\n                 .insert_many([(counter, value) for value in values],\n                              fields=[Sample.counter, Sample.value])\n                 .execute())\n\n        cte = (Sample\n               .select(Sample.counter, fn.AVG(Sample.value).alias('avg_value'))\n               .group_by(Sample.counter)\n               .cte('count_to_avg', columns=('counter', 'avg_value')))\n\n        query = (Sample\n                 .select(Sample.counter,\n                         (Sample.value - cte.c.avg_value).alias('diff'))\n                 .join(cte, on=(Sample.counter == cte.c.counter))\n                 .where(Sample.value > cte.c.avg_value)\n                 .order_by(Sample.value)\n                 .with_cte(cte))\n        self.assertEqual([(a, round(b, 2)) for a, b in query.tuples()], [\n            (1, .25),\n            (2, .2),\n            (2, .4)])\n\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL)\n    @requires_models(Sample)\n    def test_cte_with_aggregate_filter(self):\n        for i in range(1, 11):\n            Sample.create(counter=i, value=float(i * i))\n\n        cte = (Sample\n               .select(Sample.counter, Sample.value)\n               .where(Sample.counter <= 5)\n               .cte('small'))\n        query = (cte\n                 .select_from(fn.SUM(cte.c.value).alias('total'))\n                 .where(cte.c.counter > 2))\n        result = query.scalar()\n        # sum of 3^2 + 4^2 + 5^2 = 9 + 16 + 25 = 50\n        self.assertEqual(result, 50.0)\n\n\n@skip_if(not IS_SQLITE_15, 'requires row-values')\nclass TestTupleComparison(ModelTestCase):\n    requires = [User]\n\n    def test_tuples(self):\n        ua, ub, uc = [User.create(username=username) for username in 'abc']\n        query = User.select().where(\n            Tuple(User.username, User.id) == ('b', ub.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE ((\"t1\".\"username\", \"t1\".\"id\") = (?, ?))'), ['b', ub.id])\n        self.assertEqual(query.count(), 1)\n        obj = query.get()\n        self.assertEqual(obj, ub)\n\n    def test_tuple_subquery(self):\n        ua, ub, uc = [User.create(username=username) for username in 'abc']\n        UA = User.alias()\n        subquery = (UA\n                    .select(UA.username, UA.id)\n                    .where(UA.username != 'b'))\n\n        query = (User\n                 .select(User.username)\n                 .where(Tuple(User.username, User.id).in_(subquery))\n                 .order_by(User.username))\n        self.assertEqual([u.username for u in query], ['a', 'c'])\n\n    @requires_models(CPK)\n    def test_row_value_composite_key(self):\n        CPK.insert_many([('k1', 1, 1), ('k2', 2, 2), ('k3', 3, 3)]).execute()\n\n        cpk = CPK.get(CPK._meta.primary_key == ('k2', 2))\n        self.assertEqual(cpk._pk, ('k2', 2))\n\n        cpk = CPK['k3', 3]\n        self.assertEqual(cpk._pk, ('k3', 3))\n\n        uq = CPK.update(extra=20).where(CPK._meta.primary_key != ('k2', 2))\n        uq.execute()\n\n        self.assertEqual(list(sorted(CPK.select().tuples())), [\n            ('k1', 1, 20), ('k2', 2, 2), ('k3', 3, 20)])\n\n\nclass TestModelGraph(BaseTestCase):\n    def test_bind_model_database(self):\n        class User(Model): pass\n        class Tweet(Model):\n            user = ForeignKeyField(User)\n        class Relationship(Model):\n            from_user = ForeignKeyField(User, backref='relationships')\n            to_user = ForeignKeyField(User, backref='related_to')\n        class Flag(Model):\n            tweet = ForeignKeyField(Tweet)\n        class Unrelated(Model): pass\n\n        fake_db = SqliteDatabase(None)\n        User.bind(fake_db)\n        for model in (User, Tweet, Relationship, Flag):\n            self.assertTrue(model._meta.database is fake_db)\n        self.assertTrue(Unrelated._meta.database is None)\n        User.bind(None)\n\n        with User.bind_ctx(fake_db) as (FUser,):\n            self.assertTrue(FUser._meta.database is fake_db)\n            self.assertTrue(Unrelated._meta.database is None)\n\n        self.assertTrue(User._meta.database is None)\n\n\nclass TestFieldInheritance(BaseTestCase):\n    def test_field_inheritance(self):\n        class BaseModel(Model):\n            class Meta:\n                database = get_in_memory_db()\n\n        class BasePost(BaseModel):\n            content = TextField()\n            timestamp = TimestampField()\n\n        class Photo(BasePost):\n            image = TextField()\n\n        class Note(BasePost):\n            category = TextField()\n\n        self.assertEqual(BasePost._meta.sorted_field_names,\n                         ['id', 'content', 'timestamp'])\n        self.assertEqual(BasePost._meta.sorted_fields, [\n            BasePost.id,\n            BasePost.content,\n            BasePost.timestamp])\n\n        self.assertEqual(Photo._meta.sorted_field_names,\n                         ['id', 'content', 'timestamp', 'image'])\n        self.assertEqual(Photo._meta.sorted_fields, [\n            Photo.id,\n            Photo.content,\n            Photo.timestamp,\n            Photo.image])\n\n        self.assertEqual(Note._meta.sorted_field_names,\n                         ['id', 'content', 'timestamp', 'category'])\n        self.assertEqual(Note._meta.sorted_fields, [\n            Note.id,\n            Note.content,\n            Note.timestamp,\n            Note.category])\n\n        self.assertTrue(id(Photo.id) != id(Note.id))\n\n    def test_foreign_key_field_inheritance(self):\n        class BaseModel(Model):\n            class Meta:\n                database = get_in_memory_db()\n\n        class Category(BaseModel):\n            name = TextField()\n\n        class BasePost(BaseModel):\n            category = ForeignKeyField(Category)\n            timestamp = TimestampField()\n\n        class Photo(BasePost):\n            image = TextField()\n\n        class Note(BasePost):\n            content = TextField()\n\n        self.assertEqual(BasePost._meta.sorted_field_names,\n                         ['id', 'category', 'timestamp'])\n        self.assertEqual(BasePost._meta.sorted_fields, [\n            BasePost.id,\n            BasePost.category,\n            BasePost.timestamp])\n\n        self.assertEqual(Photo._meta.sorted_field_names,\n                         ['id', 'category', 'timestamp', 'image'])\n        self.assertEqual(Photo._meta.sorted_fields, [\n            Photo.id,\n            Photo.category,\n            Photo.timestamp,\n            Photo.image])\n\n        self.assertEqual(Note._meta.sorted_field_names,\n                         ['id', 'category', 'timestamp', 'content'])\n        self.assertEqual(Note._meta.sorted_fields, [\n            Note.id,\n            Note.category,\n            Note.timestamp,\n            Note.content])\n\n        self.assertEqual(Category._meta.backrefs, {\n            BasePost.category: BasePost,\n            Photo.category: Photo,\n            Note.category: Note})\n        self.assertEqual(BasePost._meta.refs, {BasePost.category: Category})\n        self.assertEqual(Photo._meta.refs, {Photo.category: Category})\n        self.assertEqual(Note._meta.refs, {Note.category: Category})\n\n        self.assertEqual(BasePost.category.backref, 'basepost_set')\n        self.assertEqual(Photo.category.backref, 'photo_set')\n        self.assertEqual(Note.category.backref, 'note_set')\n\n    def test_foreign_key_pk_inheritance(self):\n        class BaseModel(Model):\n            class Meta:\n                database = get_in_memory_db()\n        class Account(BaseModel): pass\n        class BaseUser(BaseModel):\n            account = ForeignKeyField(Account, primary_key=True)\n        class User(BaseUser):\n            username = TextField()\n        class Admin(BaseUser):\n            role = TextField()\n\n        self.assertEqual(Account._meta.backrefs, {\n            Admin.account: Admin,\n            User.account: User,\n            BaseUser.account: BaseUser})\n\n        self.assertEqual(BaseUser.account.backref, 'baseuser_set')\n        self.assertEqual(User.account.backref, 'user_set')\n        self.assertEqual(Admin.account.backref, 'admin_set')\n        self.assertTrue(Account.user_set.model is Account)\n        self.assertTrue(Account.admin_set.model is Account)\n        self.assertTrue(Account.user_set.rel_model is User)\n        self.assertTrue(Account.admin_set.rel_model is Admin)\n\n        self.assertSQL(Account._schema._create_table(), (\n            'CREATE TABLE IF NOT EXISTS \"account\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY)'), [])\n\n        self.assertSQL(User._schema._create_table(), (\n            'CREATE TABLE IF NOT EXISTS \"user\" ('\n            '\"account_id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"username\" TEXT NOT NULL, '\n            'FOREIGN KEY (\"account_id\") REFERENCES \"account\" (\"id\"))'), [])\n\n        self.assertSQL(Admin._schema._create_table(), (\n            'CREATE TABLE IF NOT EXISTS \"admin\" ('\n            '\"account_id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"role\" TEXT NOT NULL, '\n            'FOREIGN KEY (\"account_id\") REFERENCES \"account\" (\"id\"))'), [])\n\n    def test_backref_inheritance(self):\n        class Category(TestModel): pass\n        def backref(fk_field):\n            return '%ss' % fk_field.model._meta.name\n        class BasePost(TestModel):\n            category = ForeignKeyField(Category, backref=backref)\n        class Note(BasePost): pass\n        class Photo(BasePost): pass\n\n        self.assertEqual(Category._meta.backrefs, {\n            BasePost.category: BasePost,\n            Note.category: Note,\n            Photo.category: Photo})\n        self.assertEqual(BasePost.category.backref, 'baseposts')\n        self.assertEqual(Note.category.backref, 'notes')\n        self.assertEqual(Photo.category.backref, 'photos')\n        self.assertTrue(Category.baseposts.rel_model is BasePost)\n        self.assertTrue(Category.baseposts.model is Category)\n        self.assertTrue(Category.notes.rel_model is Note)\n        self.assertTrue(Category.notes.model is Category)\n        self.assertTrue(Category.photos.rel_model is Photo)\n        self.assertTrue(Category.photos.model is Category)\n\n        class BaseItem(TestModel):\n            category = ForeignKeyField(Category, backref='items')\n        class ItemA(BaseItem): pass\n        class ItemB(BaseItem): pass\n\n        self.assertEqual(BaseItem.category.backref, 'items')\n        self.assertEqual(ItemA.category.backref, 'itema_set')\n        self.assertEqual(ItemB.category.backref, 'itemb_set')\n        self.assertTrue(Category.items.rel_model is BaseItem)\n        self.assertTrue(Category.itema_set.rel_model is ItemA)\n        self.assertTrue(Category.itema_set.model is Category)\n        self.assertTrue(Category.itemb_set.rel_model is ItemB)\n        self.assertTrue(Category.itemb_set.model is Category)\n\n    @skip_if(IS_SQLITE, 'sqlite is not supported')\n    @skip_if(IS_MYSQL, 'mysql is not raising this error(?)')\n    @skip_if(IS_CRDB, 'crdb is not raising the error in this test(?)')\n    def test_deferred_fk_creation(self):\n        class B(TestModel):\n            a = DeferredForeignKey('A', null=True)\n            b = TextField()\n        class A(TestModel):\n            a = TextField()\n\n        db.create_tables([A, B])\n\n        try:\n            # Test that we can create B with null \"a_id\" column:\n            a = A.create(a='a')\n            b = B.create(b='b')\n\n            # Test that we can create B that has no corresponding A:\n            fake_a = A(id=31337)\n            b2 = B.create(a=fake_a, b='b2')\n            b2_db = B.get(B.a == fake_a)\n            self.assertEqual(b2_db.b, 'b2')\n\n            # Ensure error occurs trying to create_foreign_key.\n            with db.atomic():\n                self.assertRaises(\n                    IntegrityError,\n                    B._schema.create_foreign_key,\n                    B.a)\n\n            b2_db.delete_instance()\n\n            # We can now create the foreign key.\n            B._schema.create_foreign_key(B.a)\n\n            # The foreign-key is enforced:\n            with db.atomic():\n                self.assertRaises(IntegrityError, B.create, a=fake_a, b='b3')\n        finally:\n            db.drop_tables([A, B])\n\n\nclass TestMetaTableName(BaseTestCase):\n    def test_table_name_behavior(self):\n        def make_model(model_name, table=None):\n            class Meta:\n                legacy_table_names = False\n                table_name = table\n            return type(model_name, (Model,), {'Meta': Meta})\n        def assertTableName(expected, model_name, table_name=None):\n            model_class = make_model(model_name, table_name)\n            self.assertEqual(model_class._meta.table_name, expected)\n\n        assertTableName('users', 'User', 'users')\n        assertTableName('tweet', 'Tweet')\n        assertTableName('user_profile', 'UserProfile')\n        assertTableName('activity_log_status', 'ActivityLogStatus')\n\n        assertTableName('camel_case', 'CamelCase')\n        assertTableName('camel_camel_case', 'CamelCamelCase')\n        assertTableName('camel2_camel2_case', 'Camel2Camel2Case')\n        assertTableName('http_request', 'HTTPRequest')\n        assertTableName('api_response', 'APIResponse')\n        assertTableName('api_response', 'API_Response')\n        assertTableName('web_http_request', 'WebHTTPRequest')\n        assertTableName('get_http_response_code', 'getHTTPResponseCode')\n        assertTableName('foo_bar', 'foo_Bar')\n        assertTableName('foo_bar', 'Foo__Bar')\n\n\nclass TestMetaInheritance(BaseTestCase):\n    def test_table_name(self):\n        class Foo(Model):\n            class Meta:\n                def table_function(klass):\n                    return 'xxx_%s' % klass.__name__.lower()\n\n        class Bar(Foo): pass\n        class Baze(Foo):\n            class Meta:\n                table_name = 'yyy_baze'\n        class Biz(Baze): pass\n        class Nug(Foo):\n            class Meta:\n                def table_function(klass):\n                    return 'zzz_%s' % klass.__name__.lower()\n\n        self.assertEqual(Foo._meta.table_name, 'xxx_foo')\n        self.assertEqual(Bar._meta.table_name, 'xxx_bar')\n        self.assertEqual(Baze._meta.table_name, 'yyy_baze')\n        self.assertEqual(Biz._meta.table_name, 'xxx_biz')\n        self.assertEqual(Nug._meta.table_name, 'zzz_nug')\n\n    def test_composite_key_inheritance(self):\n        class Foo(Model):\n            key = TextField()\n            value = TextField()\n\n            class Meta:\n                primary_key = CompositeKey('key', 'value')\n\n        class Bar(Foo): pass\n        class Baze(Foo):\n            value = IntegerField()\n\n        foo = Foo(key='k1', value='v1')\n        self.assertEqual(foo.__composite_key__, ('k1', 'v1'))\n\n        bar = Bar(key='k2', value='v2')\n        self.assertEqual(bar.__composite_key__, ('k2', 'v2'))\n\n        baze = Baze(key='k3', value=3)\n        self.assertEqual(baze.__composite_key__, ('k3', 3))\n\n    def test_no_primary_key_inheritable(self):\n        class Foo(Model):\n            data = TextField()\n\n            class Meta:\n                primary_key = False\n\n        class Bar(Foo): pass\n        class Baze(Foo):\n            pk = AutoField()\n        class Zai(Foo):\n            zee = TextField(primary_key=True)\n\n        self.assertFalse(Foo._meta.primary_key)\n        self.assertEqual(Foo._meta.sorted_field_names, ['data'])\n        self.assertFalse(Bar._meta.primary_key)\n        self.assertEqual(Bar._meta.sorted_field_names, ['data'])\n\n        self.assertTrue(Baze._meta.primary_key is Baze.pk)\n        self.assertEqual(Baze._meta.sorted_field_names, ['pk', 'data'])\n\n        self.assertTrue(Zai._meta.primary_key is Zai.zee)\n        self.assertEqual(Zai._meta.sorted_field_names, ['zee', 'data'])\n\n    def test_inheritance(self):\n        db = SqliteDatabase(':memory:')\n\n        class Base(Model):\n            class Meta:\n                constraints = ['c1', 'c2']\n                database = db\n                indexes = (\n                    (('username',), True),\n                )\n                only_save_dirty = True\n                options = {'key': 'value'}\n                schema = 'magic'\n\n        class Child(Base): pass\n        class GrandChild(Child): pass\n\n        for ModelClass in (Child, GrandChild):\n            self.assertEqual(ModelClass._meta.constraints, ['c1', 'c2'])\n            self.assertTrue(ModelClass._meta.database is db)\n            self.assertEqual(ModelClass._meta.indexes, [(('username',), True)])\n            self.assertEqual(ModelClass._meta.options, {'key': 'value'})\n            self.assertTrue(ModelClass._meta.only_save_dirty)\n            self.assertEqual(ModelClass._meta.schema, 'magic')\n\n        class Overrides(Base):\n            class Meta:\n                constraints = None\n                indexes = None\n                only_save_dirty = False\n                options = {'foo': 'bar'}\n                schema = None\n\n        self.assertTrue(Overrides._meta.constraints is None)\n        self.assertEqual(Overrides._meta.indexes, [])\n        self.assertFalse(Overrides._meta.only_save_dirty)\n        self.assertEqual(Overrides._meta.options, {'foo': 'bar'})\n        self.assertTrue(Overrides._meta.schema is None)\n\n    def test_temporary_inheritance(self):\n        class T0(TestModel): pass\n        class T1(TestModel):\n            class Meta:\n                temporary = True\n\n        class T2(T1): pass\n        class T3(T1):\n            class Meta:\n                temporary = False\n\n        self.assertFalse(T0._meta.temporary)\n        self.assertTrue(T1._meta.temporary)\n        self.assertTrue(T2._meta.temporary)\n        self.assertFalse(T3._meta.temporary)\n\n\nclass TestModelMetadataMisc(BaseTestCase):\n    database = get_in_memory_db()\n\n    def test_subclass_aware_metadata(self):\n        class SchemaPropagateMetadata(SubclassAwareMetadata):\n            @property\n            def schema(self):\n                return self._schema\n            @schema.setter\n            def schema(self, value):\n                # self.models is a singleton, essentially, shared among all\n                # classes that use this metadata implementation.\n                for model in self.models:\n                    model._meta._schema = value\n\n        class Base(Model):\n            class Meta:\n                database = self.database\n                model_metadata_class = SchemaPropagateMetadata\n\n        class User(Base):\n            username = TextField()\n        class Tweet(Base):\n            user = ForeignKeyField(User, backref='tweets')\n            content = TextField()\n\n        self.assertTrue(User._meta.schema is None)\n        self.assertTrue(Tweet._meta.schema is None)\n\n        Base._meta.schema = 'temp'\n        self.assertEqual(User._meta.schema, 'temp')\n        self.assertEqual(Tweet._meta.schema, 'temp')\n\n        User._meta.schema = None\n        for model in (Base, User, Tweet):\n            self.assertTrue(model._meta.schema is None)\n\n\nclass TestModelSetDatabase(BaseTestCase):\n    def test_set_database(self):\n        class Register(Model):\n            value = IntegerField()\n\n        db_a = get_in_memory_db()\n        db_b = get_in_memory_db()\n        Register._meta.set_database(db_a)\n        Register.create_table()\n        Register._meta.set_database(db_b)\n        self.assertFalse(Register.table_exists())\n        self.assertEqual(db_a.get_tables(), ['register'])\n        self.assertEqual(db_b.get_tables(), [])\n        db_a.close()\n        db_b.close()\n\n\nclass TestForeignKeyFieldDescriptors(BaseTestCase):\n    def test_foreign_key_field_descriptors(self):\n        class User(Model): pass\n        class T0(Model):\n            user = ForeignKeyField(User)\n        class T1(Model):\n            user = ForeignKeyField(User, column_name='uid')\n        class T2(Model):\n            user = ForeignKeyField(User, object_id_name='uid')\n        class T3(Model):\n            user = ForeignKeyField(User, column_name='x', object_id_name='uid')\n        class T4(Model):\n            foo = ForeignKeyField(User, column_name='user')\n        class T5(Model):\n            foo = ForeignKeyField(User, object_id_name='uid')\n\n        self.assertEqual(T0.user.object_id_name, 'user_id')\n        self.assertEqual(T1.user.object_id_name, 'uid')\n        self.assertEqual(T2.user.object_id_name, 'uid')\n        self.assertEqual(T3.user.object_id_name, 'uid')\n        self.assertEqual(T4.foo.object_id_name, 'user')\n        self.assertEqual(T5.foo.object_id_name, 'uid')\n\n        user = User(id=1337)\n        self.assertEqual(T0(user=user).user_id, 1337)\n        self.assertEqual(T1(user=user).uid, 1337)\n        self.assertEqual(T2(user=user).uid, 1337)\n        self.assertEqual(T3(user=user).uid, 1337)\n        self.assertEqual(T4(foo=user).user, 1337)\n        self.assertEqual(T5(foo=user).uid, 1337)\n\n        def conflicts_with_field():\n            class TE(Model):\n                user = ForeignKeyField(User, object_id_name='user')\n\n        self.assertRaises(ValueError, conflicts_with_field)\n\n    def test_column_name(self):\n        class User(Model): pass\n        class T1(Model):\n            user = ForeignKeyField(User, column_name='user')\n\n        self.assertEqual(T1.user.column_name, 'user')\n        self.assertEqual(T1.user.object_id_name, 'user_id')\n\n\nclass TestModelAliasFieldProperties(ModelTestCase):\n    database = get_in_memory_db()\n\n    def test_field_properties(self):\n        class Person(TestModel):\n            name = TextField()\n            dob = DateField()\n            class Meta:\n                database = self.database\n\n        class Job(TestModel):\n            worker = ForeignKeyField(Person, backref='jobs')\n            client = ForeignKeyField(Person, backref='jobs_hired')\n            class Meta:\n                database = self.database\n\n        Worker = Person.alias()\n        Client = Person.alias()\n\n        expected_sql = (\n            'SELECT \"t1\".\"id\", \"t1\".\"worker_id\", \"t1\".\"client_id\" '\n            'FROM \"job\" AS \"t1\" '\n            'INNER JOIN \"person\" AS \"t2\" ON (\"t1\".\"client_id\" = \"t2\".\"id\") '\n            'INNER JOIN \"person\" AS \"t3\" ON (\"t1\".\"worker_id\" = \"t3\".\"id\") '\n            'WHERE (date_part(?, \"t2\".\"dob\") = ?)')\n        expected_params = ['year', 1983]\n\n        query = (Job\n                 .select()\n                 .join(Client, on=(Job.client == Client.id))\n                 .switch(Job)\n                 .join(Worker, on=(Job.worker == Worker.id))\n                 .where(Client.dob.year == 1983))\n        self.assertSQL(query, expected_sql, expected_params)\n\n        query = (Job\n                 .select()\n                 .join(Client, on=(Job.client == Client.id))\n                 .switch(Job)\n                 .join(Person, on=(Job.worker == Person.id))\n                 .where(Client.dob.year == 1983))\n        self.assertSQL(query, expected_sql, expected_params)\n\n        query = (Job\n                 .select()\n                 .join(Person, on=(Job.client == Person.id))\n                 .switch(Job)\n                 .join(Worker, on=(Job.worker == Worker.id))\n                 .where(Person.dob.year == 1983))\n        self.assertSQL(query, expected_sql, expected_params)\n\n\nclass OnConflictTests(object):\n    requires = [Emp]\n    test_data = (\n        ('huey', 'cat', '123'),\n        ('zaizee', 'cat', '124'),\n        ('mickey', 'dog', '125'),\n    )\n\n    def setUp(self):\n        super(OnConflictTests, self).setUp()\n        for first, last, empno in self.test_data:\n            Emp.create(first=first, last=last, empno=empno)\n\n    def assertData(self, expected):\n        query = (Emp\n                 .select(Emp.first, Emp.last, Emp.empno)\n                 .order_by(Emp.id)\n                 .tuples())\n        self.assertEqual(list(query), expected)\n\n    def test_ignore(self):\n        query = (Emp\n                 .insert(first='foo', last='bar', empno='123')\n                 .on_conflict('ignore')\n                 .execute())\n        self.assertData(list(self.test_data))\n\n\ndef requires_upsert(m):\n    return skip_unless(IS_SQLITE_24 or IS_POSTGRESQL or IS_CRDB,\n                       'requires upsert')(m)\n\n\nclass KV(TestModel):\n    key = CharField(unique=True)\n    value = IntegerField()\n\n\nclass PGOnConflictTests(OnConflictTests):\n    @requires_upsert\n    def test_update(self):\n        # Conflict on empno - we'll preserve name and update the ID. This will\n        # overwrite the previous row and set a new ID.\n        res = (Emp\n               .insert(first='foo', last='bar', empno='125')\n               .on_conflict(\n                   conflict_target=(Emp.empno,),\n                   preserve=(Emp.first, Emp.last),\n                   update={Emp.empno: '125.1'})\n               .execute())\n        self.assertData([\n            ('huey', 'cat', '123'),\n            ('zaizee', 'cat', '124'),\n            ('foo', 'bar', '125.1')])\n\n        # Conflicts on first/last name. The first name is preserved while the\n        # last-name is updated. The new empno is thrown out.\n        res = (Emp\n               .insert(first='foo', last='bar', empno='126')\n               .on_conflict(\n                   conflict_target=(Emp.first, Emp.last),\n                   preserve=(Emp.first,),\n                   update={Emp.last: 'baze'})\n               .execute())\n        self.assertData([\n            ('huey', 'cat', '123'),\n            ('zaizee', 'cat', '124'),\n            ('foo', 'baze', '125.1')])\n\n    @requires_upsert\n    @requires_models(OCTest)\n    def test_update_ignore_with_conflict_target(self):\n        query = OCTest.insert(a='foo', b=1).on_conflict(\n            action='IGNORE',\n            conflict_target=(OCTest.a,))\n        rowid1 = query.execute()\n        self.assertTrue(rowid1 is not None)\n\n        query.clone().execute()  # Nothing happens, insert is ignored.\n        self.assertEqual(OCTest.select().count(), 1)\n\n        OCTest.insert(a='foo', b=2).on_conflict_ignore().execute()\n        self.assertEqual(OCTest.select().count(), 1)\n\n        OCTest.insert(a='bar', b=1).on_conflict_ignore().execute()\n        self.assertEqual(OCTest.select().count(), 2)\n\n    @requires_upsert\n    @requires_models(OCTest)\n    def test_update_atomic(self):\n        # Add a new row with the given \"a\" value. If a conflict occurs,\n        # re-insert with b=b+2.\n        query = OCTest.insert(a='foo', b=1).on_conflict(\n            conflict_target=(OCTest.a,),\n            update={OCTest.b: OCTest.b + 2})\n\n        # First execution returns rowid=1. Second execution hits the conflict-\n        # resolution, and will update the value in \"b\" from 1 -> 3.\n        rowid1 = query.execute()\n        rowid2 = query.clone().execute()\n        self.assertEqual(rowid1, rowid2)\n\n        obj = OCTest.get()\n        self.assertEqual(obj.a, 'foo')\n        self.assertEqual(obj.b, 3)\n\n        query = OCTest.insert(a='foo', b=4, c=5).on_conflict(\n            conflict_target=[OCTest.a],\n            preserve=[OCTest.c],\n            update={OCTest.b: OCTest.b + 100})\n        self.assertEqual(query.execute(), rowid2)\n\n        obj = OCTest.get()\n        self.assertEqual(obj.a, 'foo')\n        self.assertEqual(obj.b, 103)\n        self.assertEqual(obj.c, 5)\n\n    @requires_upsert\n    @requires_models(OCTest)\n    def test_update_where_clause(self):\n        # Add a new row with the given \"a\" value. If a conflict occurs,\n        # re-insert with b=b+2 so long as the original b < 3.\n        query = OCTest.insert(a='foo', b=1).on_conflict(\n            conflict_target=(OCTest.a,),\n            update={OCTest.b: OCTest.b + 2},\n            where=(OCTest.b < 3))\n\n        # First execution returns rowid=1. Second execution hits the conflict-\n        # resolution, and will update the value in \"b\" from 1 -> 3.\n        rowid1 = query.execute()\n        rowid2 = query.clone().execute()\n        self.assertEqual(rowid1, rowid2)\n\n        obj = OCTest.get()\n        self.assertEqual(obj.a, 'foo')\n        self.assertEqual(obj.b, 3)\n\n        # Third execution also returns rowid=1. The WHERE clause prevents us\n        # from updating \"b\" again. If this is SQLite, we get the rowid back, if\n        # this is Postgresql we get None (since nothing happened).\n        rowid3 = query.clone().execute()\n        if IS_SQLITE:\n            self.assertEqual(rowid1, rowid3)\n        else:\n            self.assertTrue(rowid3 is None)\n\n        # Because we didn't satisfy the WHERE clause, the value in \"b\" is\n        # not incremented again.\n        obj = OCTest.get()\n        self.assertEqual(obj.a, 'foo')\n        self.assertEqual(obj.b, 3)\n\n    @requires_upsert\n    @requires_models(Emp)  # Has unique on first/last, unique on empno.\n    def test_conflict_update_excluded(self):\n        e1 = Emp.create(first='huey', last='c', empno='10')\n        e2 = Emp.create(first='zaizee', last='c', empno='20')\n\n        res = (Emp.insert(first='huey', last='c', empno='30')\n               .on_conflict(conflict_target=(Emp.first, Emp.last),\n                            update={Emp.empno: Emp.empno + EXCLUDED.empno},\n                            where=(EXCLUDED.empno != Emp.empno))\n               .execute())\n\n        data = sorted(Emp.select(Emp.first, Emp.last, Emp.empno).tuples())\n        self.assertEqual(data, [('huey', 'c', '1030'), ('zaizee', 'c', '20')])\n\n    @requires_upsert\n    @requires_models(KV)\n    def test_conflict_update_excluded2(self):\n        KV.create(key='k1', value=1)\n\n        query = (KV.insert(key='k1', value=10)\n                 .on_conflict(conflict_target=[KV.key],\n                              update={KV.value: KV.value + EXCLUDED.value},\n                              where=(EXCLUDED.value > KV.value)))\n        query.execute()\n        self.assertEqual(KV.select(KV.key, KV.value).tuples()[:], [('k1', 11)])\n\n        # Running it again will have no effect this time, since the new value\n        # (10) is not greater than the pre-existing row value (11).\n        query.execute()\n        self.assertEqual(KV.select(KV.key, KV.value).tuples()[:], [('k1', 11)])\n\n    @requires_upsert\n    @skip_if(IS_CRDB, 'crdb does not support the WHERE clause')\n    @requires_models(UKVP)\n    def test_conflict_target_constraint_where(self):\n        u1 = UKVP.create(key='k1', value=1, extra=1)\n        u2 = UKVP.create(key='k2', value=2, extra=2)\n\n        fields = [UKVP.key, UKVP.value, UKVP.extra]\n        data = [('k1', 1, 2), ('k2', 2, 3)]\n\n        # XXX: SQLite does not seem to accept parameterized values for the\n        # conflict target WHERE clause (e.g., the partial index). So we have to\n        # express this literally as (\"extra\" > 1) rather than using an\n        # expression which will be parameterized. Hopefully SQLite's authors\n        # decide this is a bug and fix it.\n        if IS_SQLITE:\n            conflict_where = UKVP.extra > SQL('1')\n        else:\n            conflict_where = UKVP.extra > 1\n\n        res = (UKVP.insert_many(data, fields)\n               .on_conflict(conflict_target=(UKVP.key, UKVP.value),\n                            conflict_where=conflict_where,\n                            preserve=(UKVP.extra,))\n               .execute())\n\n        # How many rows exist? The first one would not have triggered the\n        # conflict resolution, since the existing k1/1 row's \"extra\" value was\n        # not greater than 1, thus it did not satisfy the index condition.\n        # The second row (k2/2/3) would have triggered the resolution.\n        self.assertEqual(UKVP.select().count(), 3)\n        query = (UKVP\n                 .select(UKVP.key, UKVP.value, UKVP.extra)\n                 .order_by(UKVP.key, UKVP.value, UKVP.extra)\n                 .tuples())\n\n        self.assertEqual(list(query), [\n            ('k1', 1, 1),\n            ('k1', 1, 2),\n            ('k2', 2, 3)])\n\n        # Verify the primary-key of k2 did not change.\n        u2_db = UKVP.get(UKVP.key == 'k2')\n        self.assertEqual(u2_db.id, u2.id)\n\n\n@requires_mysql\nclass TestUpsertMySQL(OnConflictTests, ModelTestCase):\n    def test_replace(self):\n        # Unique constraint on first/last would fail - replace.\n        query = (Emp\n                 .insert(first='mickey', last='dog', empno='1337')\n                 .on_conflict('replace')\n                 .execute())\n        self.assertData([\n            ('huey', 'cat', '123'),\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337')])\n\n        # Unique constraint on empno would fail - replace.\n        query = (Emp\n                 .insert(first='nuggie', last='dog', empno='123')\n                 .on_conflict('replace')\n                 .execute())\n        self.assertData([\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337'),\n            ('nuggie', 'dog', '123')])\n\n        # No problems, data added.\n        query = (Emp\n                 .insert(first='beanie', last='cat', empno='126')\n                 .on_conflict('replace')\n                 .execute())\n        self.assertData([\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337'),\n            ('nuggie', 'dog', '123'),\n            ('beanie', 'cat', '126')])\n\n    @requires_models(OCTest)\n    def test_update(self):\n        pk = (OCTest\n              .insert(a='a', b=3)\n              .on_conflict(update={OCTest.b: 1337})\n              .execute())\n        oc = OCTest.get(OCTest.a == 'a')\n        self.assertEqual(oc.b, 3)\n\n        pk2 = (OCTest\n               .insert(a='a', b=4)\n               .on_conflict(update={OCTest.b: OCTest.b + 10})\n               .execute())\n        self.assertEqual(pk, pk2)\n        self.assertEqual(OCTest.select().count(), 1)\n\n        oc = OCTest.get(OCTest.a == 'a')\n        self.assertEqual(oc.b, 13)\n\n        pk3 = (OCTest\n               .insert(a='a2', b=5)\n               .on_conflict(update={OCTest.b: 1337})\n               .execute())\n        self.assertTrue(pk3 != pk2)\n        self.assertEqual(OCTest.select().count(), 2)\n\n        oc = OCTest.get(OCTest.a == 'a2')\n        self.assertEqual(oc.b, 5)\n\n    @requires_models(OCTest)\n    def test_update_preserve(self):\n        OCTest.create(a='a', b=3)\n\n        pk = (OCTest\n              .insert(a='a', b=4)\n              .on_conflict(preserve=[OCTest.b])\n              .execute())\n        oc = OCTest.get(OCTest.a == 'a')\n        self.assertEqual(oc.b, 4)\n\n        pk2 = (OCTest\n               .insert(a='a', b=5, c=6)\n               .on_conflict(\n                   preserve=[OCTest.c],\n                   update={OCTest.b: OCTest.b + 100})\n               .execute())\n        self.assertEqual(pk, pk2)\n        self.assertEqual(OCTest.select().count(), 1)\n\n        oc = OCTest.get(OCTest.a == 'a')\n        self.assertEqual(oc.b, 104)\n        self.assertEqual(oc.c, 6)\n\n\nclass TestReplaceSqlite(OnConflictTests, ModelTestCase):\n    database = get_in_memory_db()\n\n    def test_replace(self):\n        # Unique constraint on first/last would fail - replace.\n        query = (Emp\n                 .insert(first='mickey', last='dog', empno='1337')\n                 .on_conflict('replace')\n                 .execute())\n        self.assertData([\n            ('huey', 'cat', '123'),\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337')])\n\n        # Unique constraint on empno would fail - replace.\n        query = (Emp\n                 .insert(first='nuggie', last='dog', empno='123')\n                 .on_conflict('replace')\n                 .execute())\n        self.assertData([\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337'),\n            ('nuggie', 'dog', '123')])\n\n        # No problems, data added.\n        query = (Emp\n                 .insert(first='beanie', last='cat', empno='126')\n                 .on_conflict('replace')\n                 .execute())\n        self.assertData([\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337'),\n            ('nuggie', 'dog', '123'),\n            ('beanie', 'cat', '126')])\n\n    def test_model_replace(self):\n        Emp.replace(first='mickey', last='dog', empno='1337').execute()\n        self.assertData([\n            ('huey', 'cat', '123'),\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337')])\n\n        Emp.replace(first='beanie', last='cat', empno='999').execute()\n        self.assertData([\n            ('huey', 'cat', '123'),\n            ('zaizee', 'cat', '124'),\n            ('mickey', 'dog', '1337'),\n            ('beanie', 'cat', '999')])\n\n        Emp.replace_many([('h', 'cat', '123'), ('z', 'cat', '124'),\n                          ('b', 'cat', '125')],\n                         fields=[Emp.first, Emp.last, Emp.empno]).execute()\n        self.assertData([\n            ('mickey', 'dog', '1337'),\n            ('beanie', 'cat', '999'),\n            ('h', 'cat', '123'),\n            ('z', 'cat', '124'),\n            ('b', 'cat', '125')])\n\n\n@requires_sqlite\nclass TestUpsertSqlite(PGOnConflictTests, ModelTestCase):\n    database = get_in_memory_db()\n\n    @skip_if(IS_SQLITE_24, 'requires sqlite < 3.24')\n    def test_no_preserve_update_where(self):\n        # Ensure on SQLite < 3.24 we cannot update or preserve values.\n        base = Emp.insert(first='foo', last='bar', empno='125')\n\n        preserve = base.on_conflict(preserve=[Emp.last])\n        self.assertRaises(ValueError, preserve.execute)\n\n        update = base.on_conflict(update={Emp.empno: 'xxx'})\n        self.assertRaises(ValueError, update.execute)\n\n        where = base.on_conflict(where=(Emp.id > 10))\n        self.assertRaises(ValueError, where.execute)\n\n    @skip_unless(IS_SQLITE_24, 'requires sqlite >= 3.24')\n    def test_update_meets_requirements(self):\n        # Ensure that on >= 3.24 any updates meet the minimum criteria.\n        base = Emp.insert(first='foo', last='bar', empno='125')\n\n        # Must specify update or preserve.\n        no_update_preserve = base.on_conflict(conflict_target=(Emp.empno,))\n        self.assertRaises(ValueError, no_update_preserve.execute)\n\n        # Must specify a conflict target.\n        no_conflict_target = base.on_conflict(update={Emp.empno: '125.1'})\n        self.assertRaises(ValueError, no_conflict_target.execute)\n\n    @skip_unless(IS_SQLITE_24, 'requires sqlite >= 3.24')\n    def test_do_nothing(self):\n        query = (Emp\n                 .insert(first='foo', last='bar', empno='123')\n                 .on_conflict('nothing'))\n        self.assertSQL(query, (\n            'INSERT INTO \"emp\" (\"first\", \"last\", \"empno\") '\n            'VALUES (?, ?, ?) ON CONFLICT DO NOTHING'), ['foo', 'bar', '123'])\n\n        query.execute()  # Conflict occurs with empno='123'.\n        self.assertData(list(self.test_data))\n\n\nclass UKV(TestModel):\n    key = TextField()\n    value = TextField()\n    extra = TextField(default='')\n\n    class Meta:\n        constraints = [\n            SQL('constraint ukv_key_value unique(key, value)'),\n        ]\n\n\nclass UKVRel(TestModel):\n    key = TextField()\n    value = TextField()\n    extra = TextField()\n\n    class Meta:\n        indexes = (\n            (('key', 'value'), True),\n        )\n\n\n@requires_pglike\nclass TestUpsertPostgresql(PGOnConflictTests, ModelTestCase):\n    @requires_postgresql\n    @requires_models(UKV)\n    def test_conflict_target_constraint(self):\n        u1 = UKV.create(key='k1', value='v1')\n        u2 = UKV.create(key='k2', value='v2')\n\n        ret = (UKV.insert(key='k1', value='v1', extra='e1')\n               .on_conflict(conflict_target=(UKV.key, UKV.value),\n                            preserve=(UKV.extra,))\n               .execute())\n        self.assertEqual(ret, u1.id)\n\n        # Changes were saved successfully.\n        u1_db = UKV.get(UKV.key == 'k1')\n        self.assertEqual(u1_db.key, 'k1')\n        self.assertEqual(u1_db.value, 'v1')\n        self.assertEqual(u1_db.extra, 'e1')\n        self.assertEqual(UKV.select().count(), 2)\n\n        ret = (UKV.insert(key='k2', value='v2', extra='e2')\n               .on_conflict(conflict_constraint='ukv_key_value',\n                            preserve=(UKV.extra,))\n               .execute())\n        self.assertEqual(ret, u2.id)\n\n        # Changes were saved successfully.\n        u2_db = UKV.get(UKV.key == 'k2')\n        self.assertEqual(u2_db.key, 'k2')\n        self.assertEqual(u2_db.value, 'v2')\n        self.assertEqual(u2_db.extra, 'e2')\n        self.assertEqual(UKV.select().count(), 2)\n\n        ret = (UKV.insert(key='k3', value='v3', extra='e3')\n               .on_conflict(conflict_target=[UKV.key, UKV.value],\n                            preserve=[UKV.extra])\n               .execute())\n        self.assertTrue(ret > u2_db.id)\n        self.assertEqual(UKV.select().count(), 3)\n\n    @requires_models(UKV, UKVRel)\n    def test_conflict_ambiguous_column(self):\n        # k1/v1/e1, k2/v2/e0, k3/v3/e1\n        for i in [1, 2, 3]:\n            UKV.create(key='k%s' % i, value='v%s' % i, extra='e%s' % (i % 2))\n\n        UKVRel.create(key='k1', value='v1', extra='x1')\n        UKVRel.create(key='k2', value='v2', extra='x2')\n\n        subq = UKV.select(UKV.key, UKV.value, UKV.extra)\n        query = (UKVRel\n                 .insert_from(subq, [UKVRel.key, UKVRel.value, UKVRel.extra])\n                 .on_conflict(conflict_target=[UKVRel.key, UKVRel.value],\n                              preserve=[UKVRel.extra],\n                              where=(UKVRel.key != 'k2')))\n        self.assertSQL(query, (\n            'INSERT INTO \"ukv_rel\" (\"key\", \"value\", \"extra\") '\n            'SELECT \"t1\".\"key\", \"t1\".\"value\", \"t1\".\"extra\" FROM \"ukv\" AS \"t1\" '\n            'ON CONFLICT (\"key\", \"value\") DO UPDATE '\n            'SET \"extra\" = EXCLUDED.\"extra\" '\n            'WHERE (\"ukv_rel\".\"key\" != ?) RETURNING \"ukv_rel\".\"id\"'), ['k2'])\n\n        query.execute()\n        query = (UKVRel\n                 .select(UKVRel.key, UKVRel.value, UKVRel.extra)\n                 .order_by(UKVRel.key))\n        self.assertEqual(list(query.tuples()), [\n            ('k1', 'v1', 'e1'),\n            ('k2', 'v2', 'x2'),\n            ('k3', 'v3', 'e1')])\n\n    @requires_models(Emp)\n    def test_upsert_preserves_existing(self):\n        #Emp.create(first='beanie', last='cat', empno='998')\n        Emp.create(first='beanie', last='cat', empno='999')\n        (Emp\n         .insert(first='huey', last='kitten', empno='999')\n         .on_conflict(\n             conflict_target=(Emp.empno,),\n             preserve=(Emp.last,))\n         .execute())\n        obj = Emp.get(Emp.empno == '999')\n        self.assertEqual(obj.first, 'beanie')\n        # last was NOT preserved, so it gets the val from the insert.\n        self.assertEqual(obj.last, 'kitten')\n\n    @requires_models(Emp)\n    def test_upsert_update_expression(self):\n        Emp.create(first='huey', last='cat', empno='999')\n        (Emp\n         .insert(first='hueky', last='kitten', empno='999')\n         .on_conflict(\n             conflict_target=(Emp.empno,),\n             update={Emp.first: Emp.first + 'yyy',\n                     Emp.last: Emp.last + 'lands'})\n         .execute())\n        obj = Emp.get(Emp.empno == '999')\n        self.assertEqual(obj.first, 'hueyyyy')\n        self.assertEqual(obj.last, 'catlands')\n\n\nclass TestJoinSubquery(ModelTestCase):\n    requires = [Person, Relationship]\n\n    def test_join_subquery(self):\n        # Set up some relationships such that there exists a relationship from\n        # the left-hand to the right-hand name.\n        data = (\n            ('charlie', None),\n            ('huey', 'charlie'),\n            ('mickey', 'charlie'),\n            ('zaizee', 'charlie'),\n            ('zaizee', 'huey'))\n        people = {}\n        def get_person(name):\n            if name not in people:\n                people[name] = Person.create(first=name, last=name,\n                                             dob=datetime.date(2017, 1, 1))\n            return people[name]\n\n        for person, related_to in data:\n            p1 = get_person(person)\n            if related_to is not None:\n                p2 = get_person(related_to)\n                Relationship.create(from_person=p1, to_person=p2)\n\n        # Create the subquery.\n        Friend = Person.alias('friend')\n        subq = (Relationship\n                .select(Friend.first.alias('friend_name'),\n                        Relationship.from_person)\n                .join(Friend, on=(Relationship.to_person == Friend.id))\n                .alias('subq'))\n\n        # Outer query does a LEFT OUTER JOIN. We join on the subquery because\n        # it uses an INNER JOIN, saving us doing two LEFT OUTER joins in the\n        # single query.\n        query = (Person\n                 .select(Person.first, subq.c.friend_name)\n                 .join(subq, JOIN.LEFT_OUTER,\n                       on=(Person.id == subq.c.from_person_id))\n                 .order_by(Person.first, subq.c.friend_name))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"first\", \"subq\".\"friend_name\" '\n            'FROM \"person\" AS \"t1\" '\n            'LEFT OUTER JOIN ('\n            'SELECT \"friend\".\"first\" AS \"friend_name\", \"t2\".\"from_person_id\" '\n            'FROM \"relationship\" AS \"t2\" '\n            'INNER JOIN \"person\" AS \"friend\" '\n            'ON (\"t2\".\"to_person_id\" = \"friend\".\"id\")) AS \"subq\" '\n            'ON (\"t1\".\"id\" = \"subq\".\"from_person_id\") '\n            'ORDER BY \"t1\".\"first\", \"subq\".\"friend_name\"'), [])\n\n        db_data = [row for row in query.tuples()]\n        self.assertEqual(db_data, list(data))\n\n\nclass TestSumCase(ModelTestCase):\n    @requires_models(User)\n    def test_sum_case(self):\n        for username in ('charlie', 'huey', 'zaizee'):\n            User.create(username=username)\n\n        case = Case(None, [(User.username.endswith('e'), 1)], 0)\n        e_sum = fn.SUM(case)\n        query = (User\n                 .select(User.username, e_sum.alias('e_sum'))\n                 .group_by(User.username)\n                 .order_by(User.username))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\", '\n            'SUM(CASE WHEN (\"t1\".\"username\" ILIKE ?) THEN ? ELSE ? END) '\n            'AS \"e_sum\" '\n            'FROM \"users\" AS \"t1\" '\n            'GROUP BY \"t1\".\"username\" '\n            'ORDER BY \"t1\".\"username\"'), ['%e', 1, 0])\n\n        data = [(user.username, user.e_sum) for user in query]\n        self.assertEqual(data, [\n            ('charlie', 1),\n            ('huey', 0),\n            ('zaizee', 1)])\n\n\nclass TUser(TestModel):\n    username = TextField()\n\n\nclass Transaction(TestModel):\n    user = ForeignKeyField(TUser, backref='transactions')\n    amount = FloatField(default=0.)\n\n\nclass TestMaxAlias(ModelTestCase):\n    requires = [Transaction, TUser]\n\n    def test_max_alias(self):\n        with self.database.atomic():\n            charlie = TUser.create(username='charlie')\n            huey = TUser.create(username='huey')\n\n            data = (\n                (charlie, 10.),\n                (charlie, 20.),\n                (charlie, 30.),\n                (huey, 1.5),\n                (huey, 2.5))\n            for user, amount in data:\n                Transaction.create(user=user, amount=amount)\n\n        with self.assertQueryCount(1):\n            amount = fn.MAX(Transaction.amount).alias('amount')\n            query = (Transaction\n                     .select(amount, TUser.username)\n                     .join(TUser)\n                     .group_by(TUser.username)\n                     .order_by(TUser.username))\n            data = [(txn.amount, txn.user.username) for txn in query]\n\n        self.assertEqual(data, [\n            (30., 'charlie'),\n            (2.5, 'huey')])\n\n\nclass CNote(TestModel):\n    content = TextField()\n    timestamp = TimestampField()\n\nclass CFile(TestModel):\n    filename = CharField(primary_key=True)\n    data = TextField()\n    timestamp = TimestampField()\n\n\nclass TestCompoundSelectModels(ModelTestCase):\n    requires = [CFile, CNote]\n\n    def setUp(self):\n        super(TestCompoundSelectModels, self).setUp()\n        def generate_ts():\n            i = [0]\n            def _inner():\n                i[0] += 1\n                return datetime.datetime(2018, 1, i[0])\n            return _inner\n        make_ts = generate_ts()\n        self.ts = lambda i: datetime.datetime(2018, 1, i)\n\n        with self.database.atomic():\n            for i, content in enumerate(('note-a', 'note-b', 'note-c'), 1):\n                CNote.create(id=i, content=content, timestamp=make_ts())\n\n            file_data = (\n                ('peewee.txt', 'peewee orm'),\n                ('walrus.txt', 'walrus redis toolkit'),\n                ('huey.txt', 'huey task queue'))\n            for filename, data in file_data:\n                CFile.create(filename=filename, data=data, timestamp=make_ts())\n\n    def test_mix_models_with_model_row_type(self):\n        cast = 'CHAR' if IS_MYSQL else 'TEXT'\n        lhs = CNote.select(CNote.id.cast(cast).alias('id_text'),\n                           CNote.content, CNote.timestamp)\n        rhs = CFile.select(CFile.filename, CFile.data, CFile.timestamp)\n        query = (lhs | rhs).order_by(SQL('timestamp')).limit(4)\n\n        data = [(n.id_text, n.content, n.timestamp) for n in query]\n        self.assertEqual(data, [\n            ('1', 'note-a', self.ts(1)),\n            ('2', 'note-b', self.ts(2)),\n            ('3', 'note-c', self.ts(3)),\n            ('peewee.txt', 'peewee orm', self.ts(4))])\n\n    def test_mixed_models_tuple_row_type(self):\n        cast = 'CHAR' if IS_MYSQL else 'TEXT'\n        lhs = CNote.select(CNote.id.cast(cast).alias('id'),\n                           CNote.content, CNote.timestamp)\n        rhs = CFile.select(CFile.filename, CFile.data, CFile.timestamp)\n        query = (lhs | rhs).order_by(SQL('timestamp')).limit(5)\n\n        self.assertEqual(list(query.tuples()), [\n            ('1', 'note-a', self.ts(1)),\n            ('2', 'note-b', self.ts(2)),\n            ('3', 'note-c', self.ts(3)),\n            ('peewee.txt', 'peewee orm', self.ts(4)),\n            ('walrus.txt', 'walrus redis toolkit', self.ts(5))])\n\n    def test_mixed_models_dict_row_type(self):\n        notes = CNote.select(CNote.content, CNote.timestamp)\n        files = CFile.select(CFile.filename, CFile.timestamp)\n\n        query = (notes | files).order_by(SQL('timestamp').desc()).limit(4)\n        self.assertEqual(list(query.dicts()), [\n            {'content': 'huey.txt', 'timestamp': self.ts(6)},\n            {'content': 'walrus.txt', 'timestamp': self.ts(5)},\n            {'content': 'peewee.txt', 'timestamp': self.ts(4)},\n            {'content': 'note-c', 'timestamp': self.ts(3)}])\n\n\nclass SequenceModel(TestModel):\n    seq_id = IntegerField(sequence='seq_id_sequence')\n    key = TextField()\n\n\n@requires_pglike\nclass TestSequence(ModelTestCase):\n    requires = [SequenceModel]\n\n    def test_create_table(self):\n        query = SequenceModel._schema._create_table()\n        self.assertSQL(query, (\n            'CREATE TABLE IF NOT EXISTS \"sequence_model\" ('\n            '\"id\" SERIAL NOT NULL PRIMARY KEY, '\n            '\"seq_id\" INTEGER NOT NULL DEFAULT NEXTVAL(\\'seq_id_sequence\\'), '\n            '\"key\" TEXT NOT NULL)'), [])\n\n    def test_sequence(self):\n        for key in ('k1', 'k2', 'k3'):\n            SequenceModel.create(key=key)\n\n        s1, s2, s3 = SequenceModel.select().order_by(SequenceModel.key)\n\n        self.assertEqual(s1.seq_id, 1)\n        self.assertEqual(s2.seq_id, 2)\n        self.assertEqual(s3.seq_id, 3)\n\n\n@requires_postgresql\nclass TestUpdateFromIntegration(ModelTestCase):\n    requires = [User]\n\n    def test_update_from(self):\n        u1, u2 = [User.create(username=username) for username in ('u1', 'u2')]\n        data = [(u1.id, 'u1-x'), (u2.id, 'u2-x')]\n        vl = ValuesList(data, columns=('id', 'username'), alias='tmp')\n        (User\n         .update({User.username: vl.c.username})\n         .from_(vl)\n         .where(User.id == vl.c.id)\n         .execute())\n\n        usernames = [u.username for u in User.select().order_by(User.username)]\n        self.assertEqual(usernames, ['u1-x', 'u2-x'])\n\n    def test_update_from_subselect(self):\n        u1, u2 = [User.create(username=username) for username in ('u1', 'u2')]\n        data = [(u1.id, 'u1-y'), (u2.id, 'u2-y')]\n        vl = ValuesList(data, columns=('id', 'username'), alias='tmp')\n        subq = vl.select(vl.c.id, vl.c.username)\n        (User\n         .update({User.username: subq.c.username})\n         .from_(subq)\n         .where(User.id == subq.c.id)\n         .execute())\n\n        usernames = [u.username for u in User.select().order_by(User.username)]\n        self.assertEqual(usernames, ['u1-y', 'u2-y'])\n\n    @requires_models(User, Tweet)\n    def test_update_from_simple(self):\n        u = User.create(username='u1')\n        t1 = Tweet.create(user=u, content='t1')\n        t2 = Tweet.create(user=u, content='t2')\n\n        (User\n         .update({User.username: Tweet.content})\n         .from_(Tweet)\n         .where(Tweet.content == 't2')\n         .execute())\n\n        self.assertEqual(User.get(User.id == u.id).username, 't2')\n\n\n@requires_postgresql\nclass TestLateralJoin(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_lateral_join(self):\n        with self.database.atomic():\n            for i in range(3):\n                u = User.create(username='u%s' % i)\n                for j in range(4):\n                    Tweet.create(user=u, content='u%s-t%s' % (i, j))\n\n        # GOAL: query users and their 2 most-recent tweets (by ID).\n        TA = Tweet.alias()\n\n        # The \"outer loop\" will be iterating over the users whose tweets we are\n        # trying to find.\n        user_query = (User\n                      .select(User.id, User.username)\n                      .order_by(User.id)\n                      .alias('uq'))\n\n        # The inner loop will select tweets and is correlated to the outer loop\n        # via the WHERE clause. Note that we are using a LIMIT clause.\n        tweet_query = (TA\n                       .select(TA.id, TA.content)\n                       .where(TA.user == user_query.c.id)\n                       .order_by(TA.id.desc())\n                       .limit(2)\n                       .alias('pq'))\n\n        join = NodeList((user_query, SQL('LEFT JOIN LATERAL'), tweet_query,\n                         SQL('ON %s', [True])))\n        query = (Tweet\n                 .select(user_query.c.username, tweet_query.c.content)\n                 .from_(join)\n                 .dicts())\n        self.assertEqual([row for row in query], [\n            {'username': 'u0', 'content': 'u0-t3'},\n            {'username': 'u0', 'content': 'u0-t2'},\n            {'username': 'u1', 'content': 'u1-t3'},\n            {'username': 'u1', 'content': 'u1-t2'},\n            {'username': 'u2', 'content': 'u2-t3'},\n            {'username': 'u2', 'content': 'u2-t2'}])\n\n\nclass Task(TestModel):\n    heading = ForeignKeyField('self', backref='tasks', null=True)\n    project = ForeignKeyField('self', backref='projects', null=True)\n    title = TextField()\n    type = IntegerField()\n\n    PROJECT = 1\n    HEADING = 2\n\n\nclass TestMultiSelfJoin(ModelTestCase):\n    requires = [Task]\n\n    def setUp(self):\n        super(TestMultiSelfJoin, self).setUp()\n\n        with self.database.atomic():\n            p_dev = Task.create(title='dev', type=Task.PROJECT)\n            p_p = Task.create(title='peewee', project=p_dev, type=Task.PROJECT)\n            p_h = Task.create(title='huey', project=p_dev, type=Task.PROJECT)\n\n            heading_data = (\n                ('peewee-1', p_p, 2),\n                ('peewee-2', p_p, 0),\n                ('huey-1', p_h, 1),\n                ('huey-2', p_h, 1))\n            for title, proj, n_subtasks in heading_data:\n                t = Task.create(title=title, project=proj, type=Task.HEADING)\n                for i in range(n_subtasks):\n                    Task.create(title='%s-%s' % (title, i + 1), project=proj,\n                                heading=t, type=Task.HEADING)\n\n    def test_multi_self_join(self):\n        Project = Task.alias()\n        Heading = Task.alias()\n        query = (Task\n                 .select(Task, Project, Heading)\n                 .join(Heading, JOIN.LEFT_OUTER,\n                       on=(Task.heading == Heading.id).alias('heading'))\n                 .switch(Task)\n                 .join(Project, JOIN.LEFT_OUTER,\n                       on=(Task.project == Project.id).alias('project'))\n                 .order_by(Task.id))\n\n        with self.assertQueryCount(1):\n            accum = []\n            for task in query:\n                h_title = task.heading.title if task.heading else None\n                p_title = task.project.title if task.project else None\n                accum.append((task.title, h_title, p_title))\n\n        self.assertEqual(accum, [\n            # title - heading - project\n            ('dev', None, None),\n            ('peewee', None, 'dev'),\n            ('huey', None, 'dev'),\n            ('peewee-1', None, 'peewee'),\n            ('peewee-1-1', 'peewee-1', 'peewee'),\n            ('peewee-1-2', 'peewee-1', 'peewee'),\n            ('peewee-2', None, 'peewee'),\n            ('huey-1', None, 'huey'),\n            ('huey-1-1', 'huey-1', 'huey'),\n            ('huey-2', None, 'huey'),\n            ('huey-2-1', 'huey-2', 'huey'),\n        ])\n\n\nclass Product(TestModel):\n    name = TextField()\n    price = IntegerField()\n    flags = IntegerField(constraints=[SQL('DEFAULT 99')])\n    status = CharField(constraints=[Check(\"status IN ('a', 'b', 'c')\")])\n\n    class Meta:\n        constraints = [Check('price > 0')]\n\n\nclass TestModelConstraints(ModelTestCase):\n    requires = [Product]\n\n    def test_model_constraints(self):\n        p = Product.create(name='p1', price=1, status='a')\n        self.assertTrue(p.flags is None)\n\n        # Price was saved successfully, flags got server-side default value.\n        p_db = Product.get(Product.id == p.id)\n        self.assertEqual(p_db.price, 1)\n        self.assertEqual(p_db.flags, 99)\n        self.assertEqual(p_db.status, 'a')\n\n        # Cannot update price with invalid value, must be > 0.\n        with self.database.atomic():\n            p.price = -1\n            self.assertRaises(DatabaseError, p.save)\n\n        # Nor can we create a new product with an invalid price.\n        with self.database.atomic():\n            self.assertRaises(DatabaseError, Product.create, name='p2',\n                              price=0, status='a')\n\n        # Cannot set status to a value other than 1, 2 or 3.\n        with self.database.atomic():\n            p.price = 1\n            p.status = 'd'\n            self.assertRaises(DatabaseError, p.save)\n\n        # Cannot create a new product with invalid status.\n        with self.database.atomic():\n            self.assertRaises(DatabaseError, Product.create, name='p3',\n                              price=1, status='x')\n\n\nclass TestModelFieldReprs(BaseTestCase):\n    def test_model_reprs(self):\n        class User(Model):\n            username = TextField(primary_key=True)\n        class Tweet(Model):\n            user = ForeignKeyField(User, backref='tweets')\n            content = TextField()\n            timestamp = TimestampField()\n        class EAV(Model):\n            entity = TextField()\n            attribute = TextField()\n            value = TextField()\n            class Meta:\n                primary_key = CompositeKey('entity', 'attribute')\n        class NoPK(Model):\n            key = TextField()\n            class Meta:\n                primary_key = False\n\n        self.assertEqual(repr(User), '<Model: User>')\n        self.assertEqual(repr(Tweet), '<Model: Tweet>')\n        self.assertEqual(repr(EAV), '<Model: EAV>')\n        self.assertEqual(repr(NoPK), '<Model: NoPK>')\n\n        self.assertEqual(repr(User()), '<User: None>')\n        self.assertEqual(repr(Tweet()), '<Tweet: None>')\n        self.assertEqual(repr(EAV()), '<EAV: (None, None)>')\n        self.assertEqual(repr(NoPK()), '<NoPK: n/a>')\n\n        self.assertEqual(repr(User(username='huey')), '<User: huey>')\n        self.assertEqual(repr(Tweet(id=1337)), '<Tweet: 1337>')\n        self.assertEqual(repr(EAV(entity='e', attribute='a')),\n                         \"<EAV: ('e', 'a')>\")\n        self.assertEqual(repr(NoPK(key='k')), '<NoPK: n/a>')\n\n        self.assertEqual(repr(User.username), '<TextField: User.username>')\n        self.assertEqual(repr(Tweet.user), '<ForeignKeyField: Tweet.user>')\n        self.assertEqual(repr(EAV.entity), '<TextField: EAV.entity>')\n\n        self.assertEqual(repr(TextField()), '<TextField: (unbound)>')\n\n    def test_model_str_method(self):\n        class User(Model):\n            username = TextField(primary_key=True)\n\n            def __str__(self):\n                return self.username.title()\n\n        u = User(username='charlie')\n        self.assertEqual(repr(u), '<User: Charlie>')\n\n\nclass TestGetWithSecondDatabase(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User]\n\n    def test_get_with_second_database(self):\n        User.create(username='huey')\n        query = User.select().where(User.username == 'huey')\n        self.assertEqual(query.get().username, 'huey')\n\n        alt_db = get_in_memory_db()\n        with User.bind_ctx(alt_db):\n            User.create_table()\n\n        self.assertRaises(User.DoesNotExist, query.get, alt_db)\n        with User.bind_ctx(alt_db):\n            User.create(username='zaizee')\n\n        query = User.select().where(User.username == 'zaizee')\n        self.assertRaises(User.DoesNotExist, query.get)\n        self.assertEqual(query.get(alt_db).username, 'zaizee')\n\n\nclass TestMixModelsTables(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User]\n\n    def test_mix_models_tables(self):\n        Tbl = User._meta.table\n        self.assertEqual(Tbl.insert({Tbl.username: 'huey'}).execute(), 1)\n\n        huey = Tbl.select(User.username).get()\n        self.assertEqual(huey, {'username': 'huey'})\n\n        huey = User.select(Tbl.username).get()\n        self.assertEqual(huey.username, 'huey')\n\n        Tbl.update(username='huey-x').where(Tbl.username == 'huey').execute()\n        self.assertEqual(User.select().get().username, 'huey-x')\n\n        Tbl.delete().where(User.username == 'huey-x').execute()\n        self.assertEqual(Tbl.select().count(), 0)\n\n\nclass TestDatabaseExecuteQuery(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User]\n\n    def test_execute_query(self):\n        for username in ('huey', 'zaizee'):\n            User.create(username=username)\n\n        query = User.select().order_by(User.username.desc())\n        cursor = self.database.execute(query)\n        self.assertEqual([row[1] for row in cursor], ['zaizee', 'huey'])\n\n\nclass Datum(TestModel):\n    key = TextField()\n    value = IntegerField(null=True)\n\nclass TestNullOrdering(ModelTestCase):\n    requires = [Datum]\n\n    def test_null_ordering(self):\n        values = [('k1', 1), ('ka', None), ('k2', 2), ('kb', None)]\n        Datum.insert_many(values, fields=[Datum.key, Datum.value]).execute()\n\n        def assertOrder(ordering, expected):\n            query = Datum.select().order_by(*ordering)\n            self.assertEqual([d.key for d in query], expected)\n\n        # Ascending order.\n        nulls_last = (Datum.value.asc(nulls='last'), Datum.key)\n        assertOrder(nulls_last, ['k1', 'k2', 'ka', 'kb'])\n\n        nulls_first = (Datum.value.asc(nulls='first'), Datum.key)\n        assertOrder(nulls_first, ['ka', 'kb', 'k1', 'k2'])\n\n        # Descending order.\n        nulls_last = (Datum.value.desc(nulls='last'), Datum.key)\n        assertOrder(nulls_last, ['k2', 'k1', 'ka', 'kb'])\n\n        nulls_first = (Datum.value.desc(nulls='first'), Datum.key)\n        assertOrder(nulls_first, ['ka', 'kb', 'k2', 'k1'])\n\n        # Invalid values.\n        self.assertRaises(ValueError, Datum.value.desc, nulls='bar')\n        self.assertRaises(ValueError, Datum.value.asc, nulls='foo')\n\n\nclass Student(TestModel):\n    name = TextField()\n\nclass Course(TestModel):\n    name = TextField()\n\nclass Attendance(TestModel):\n    student = ForeignKeyField(Student)\n    course = ForeignKeyField(Course)\n\n\nclass TestManyToManyJoining(ModelTestCase):\n    requires = [Student, Course, Attendance]\n\n    def setUp(self):\n        super(TestManyToManyJoining, self).setUp()\n\n        data = (\n            ('charlie', ('eng101', 'cs101', 'cs111')),\n            ('huey', ('cats1', 'cats2', 'cats3')),\n            ('zaizee', ('cats2', 'cats3')))\n        c = {}\n        with self.database.atomic():\n            for name, courses in data:\n                student = Student.create(name=name)\n                for course in courses:\n                    if course not in c:\n                        c[course] = Course.create(name=course)\n                    Attendance.create(student=student, course=c[course])\n\n    def assertQuery(self, query):\n        with self.assertQueryCount(1):\n            query = query.order_by(Attendance.id)\n            results = [(a.student.name, a.course.name) for a in query]\n            self.assertEqual(results, [\n                ('charlie', 'eng101'),\n                ('charlie', 'cs101'),\n                ('charlie', 'cs111'),\n                ('huey', 'cats1'),\n                ('huey', 'cats2'),\n                ('zaizee', 'cats2')])\n\n    def test_join_subquery(self):\n        courses = (Course\n                   .select(Course.id, Course.name)\n                   .order_by(Course.id)\n                   .limit(5))\n        query = (Attendance\n                 .select(Attendance, Student, courses.c.name)\n                 .join_from(Attendance, Student)\n                 .join_from(Attendance, courses,\n                            on=(Attendance.course == courses.c.id)))\n        self.assertQuery(query)\n\n    @skip_if(IS_MYSQL)\n    def test_join_where_subquery(self):\n        courses = Course.select().order_by(Course.id).limit(5)\n        query = (Attendance\n                 .select(Attendance, Student, Course)\n                 .join_from(Attendance, Student)\n                 .join_from(Attendance, Course)\n                 .where(Attendance.course.in_(courses)))\n        self.assertQuery(query)\n\n\nclass TestColumnNameStripping(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Person]\n\n    def test_column_name_stripping(self):\n        d1 = datetime.date(1990, 1, 1)\n        d2 = datetime.date(1990, 1, 1)\n        p1 = Person.create(first='f1', last='l1', dob=d1)\n        p2 = Person.create(first='f2', last='l2', dob=d2)\n\n        query = Person.select(\n            fn.MIN(Person.dob),\n            fn.MAX(Person.dob).alias('mdob'))\n        # Get the row as a model.\n        row = query.get()\n        self.assertEqual(row.dob, d1)\n        self.assertEqual(row.mdob, d2)\n\n        row = query.dicts().get()\n        self.assertEqual(row['dob'], d1)\n        self.assertEqual(row['mdob'], d2)\n\n\nclass VL(TestModel):\n    n = IntegerField()\n    s = CharField()\n\n\n@skip_if(IS_SQLITE_OLD or IS_MYSQL or IS_CRDB)\nclass TestValuesListIntegration(ModelTestCase):\n    requires = [VL]\n    _data = [(1, 'one'), (2, 'two'), (3, 'three')]\n\n    def test_insert_into_select_from_vl(self):\n        vl = ValuesList(self._data)\n        cte = vl.cte('newvals', columns=['n', 's'])\n        res = (VL\n               .insert_from(cte.select(cte.c.n, cte.c.s), fields=[VL.n, VL.s])\n               .with_cte(cte)\n               .execute())\n\n        vq = VL.select().order_by(VL.n)\n        self.assertEqual([(v.n, v.s) for v in vq], self._data)\n\n    def test_update_vl_cte(self):\n        VL.insert_many(self._data).execute()\n\n        new_values = [(1, 'One'), (3, 'Three'), (4, 'Four')]\n        cte = ValuesList(new_values).cte('new_values', columns=('n', 's'))\n\n        # We have to use a subquery to update the individual column, as SQLite\n        # does not support UPDATE/FROM syntax.\n        subq = (cte\n                .select(cte.c.s)\n                .where(VL.n == cte.c.n))\n\n        # Perform the update, assigning extra the new value from the values\n        # list, and restricting the overall update using the composite pk.\n        res = (VL\n               .update(s=subq)\n               .where(VL.n.in_(cte.select(cte.c.n)))\n               .with_cte(cte)\n               .execute())\n\n        vq = VL.select().order_by(VL.n)\n        self.assertEqual([(v.n, v.s) for v in vq], [\n            (1, 'One'), (2, 'two'), (3, 'Three')])\n\n    def test_values_list(self):\n        vl = ValuesList(self._data)\n\n        query = vl.select(SQL('*'))\n        self.assertEqual(list(query.tuples().bind(self.database)), self._data)\n\n    @requires_postgresql\n    def test_values_list_named_columns(self):\n        vl = ValuesList(self._data).columns('idx', 'name')\n        query = (vl\n                 .select(vl.c.idx, vl.c.name)\n                 .order_by(vl.c.idx.desc()))\n        self.assertEqual(list(query.tuples().bind(self.database)),\n                         self._data[::-1])\n\n    def test_values_list_named_columns_in_cte(self):\n        vl = ValuesList(self._data)\n        cte = vl.cte('val', columns=('idx', 'name'))\n        query = (cte\n                 .select(cte.c.idx, cte.c.name)\n                 .order_by(cte.c.idx.desc())\n                 .with_cte(cte))\n        self.assertEqual(list(query.tuples().bind(self.database)),\n                         self._data[::-1])\n\n    def test_named_values_list(self):\n        vl = ValuesList(self._data).alias('vl')\n        query = vl.select()\n        self.assertEqual(list(query.tuples().bind(self.database)), self._data)\n\n\nclass C_Product(TestModel):\n    name = CharField()\n    price = IntegerField(default=0)\n\nclass C_Archive(TestModel):\n    name = CharField()\n    price = IntegerField(default=0)\n\nclass C_Part(TestModel):\n    part = CharField(primary_key=True)\n    sub_part = ForeignKeyField('self', null=True)\n\n@skip_unless(IS_POSTGRESQL)\nclass TestDataModifyingCTEIntegration(ModelTestCase):\n    requires = [C_Product, C_Archive, C_Part]\n\n    def setUp(self):\n        super(TestDataModifyingCTEIntegration, self).setUp()\n        for i in range(5):\n            C_Product.create(name='p%s' % i, price=i)\n        mp1_c_g = C_Part.create(part='mp1-c-g')\n        mp1_c = C_Part.create(part='mp1-c', sub_part=mp1_c_g)\n        mp1 = C_Part.create(part='mp1', sub_part=mp1_c)\n        mp2_c_g = C_Part.create(part='mp2-c-g')\n        mp2_c = C_Part.create(part='mp2-c', sub_part=mp2_c_g)\n        mp2 = C_Part.create(part='mp2', sub_part=mp2_c)\n\n    def test_data_modifying_cte_delete(self):\n        query = (C_Product.delete()\n                 .where(C_Product.price < 3)\n                 .returning(C_Product))\n        cte = query.cte('moved_rows')\n\n        src = Select((cte,), (cte.c.id, cte.c.name, cte.c.price))\n        res = (C_Archive\n               .insert_from(src, (C_Archive.id, C_Archive.name, C_Archive.price))\n               .with_cte(cte)\n               .execute())\n        self.assertEqual(len(list(res)), 3)\n\n        self.assertEqual(\n            sorted([(p.name, p.price) for p in C_Product.select()]),\n            [('p3', 3), ('p4', 4)])\n        self.assertEqual(\n            sorted([(p.name, p.price) for p in C_Archive.select()]),\n            [('p0', 0), ('p1', 1), ('p2', 2)])\n\n        base = (C_Part\n                .select(C_Part.sub_part, C_Part.part)\n                .where(C_Part.part == 'mp1')\n                .cte('included_parts', recursive=True,\n                     columns=('sub_part', 'part')))\n        PA = C_Part.alias('p')\n        recursive = (PA\n                     .select(PA.sub_part, PA.part)\n                     .join(base, on=(PA.part == base.c.sub_part)))\n        cte = base.union_all(recursive)\n\n        sq = Select((cte,), (cte.c.part,))\n        res = (C_Part.delete()\n               .where(C_Part.part.in_(sq))\n               .with_cte(cte)\n               .execute())\n\n        self.assertEqual(sorted([p.part for p in C_Part.select()]),\n                         ['mp2', 'mp2-c', 'mp2-c-g'])\n\n    def test_data_modifying_cte_update(self):\n        # Populate archive table w/copy of data in product.\n        C_Archive.insert_from(\n            C_Product.select(),\n            (C_Product.id, C_Product.name, C_Product.price)).execute()\n\n        query = (C_Product\n                 .update(price=C_Product.price * 2)\n                 .returning(C_Product.id, C_Product.name, C_Product.price))\n        cte = query.cte('t')\n\n        sq = cte.select_from(cte.c.id, cte.c.name, cte.c.price)\n        self.assertEqual(sorted([(x.name, x.price) for x in sq]), [\n            ('p0', 0), ('p1', 2), ('p2', 4), ('p3', 6), ('p4', 8)])\n\n        # Ensure changes were persisted.\n        self.assertEqual(sorted([(x.name, x.price) for x in C_Product]), [\n            ('p0', 0), ('p1', 2), ('p2', 4), ('p3', 6), ('p4', 8)])\n\n        sq = Select((cte,), (cte.c.id, cte.c.price))\n        res = (C_Archive\n               .update(price=sq.c.price)\n               .from_(sq)\n               .where(C_Archive.id == sq.c.id)\n               .with_cte(cte)\n               .execute())\n\n        self.assertEqual(sorted([(x.name, x.price) for x in C_Product]), [\n            ('p0', 0), ('p1', 4), ('p2', 8), ('p3', 12), ('p4', 16)])\n        self.assertEqual(sorted([(x.name, x.price) for x in C_Archive]), [\n            ('p0', 0), ('p1', 4), ('p2', 8), ('p3', 12), ('p4', 16)])\n\n    def test_data_modifying_cte_insert(self):\n        query = (C_Product\n                 .insert({'name': 'p5', 'price': 5})\n                 .returning(C_Product.id, C_Product.name, C_Product.price))\n        cte = query.cte('t')\n\n        sq = cte.select_from(cte.c.id, cte.c.name, cte.c.price)\n        self.assertEqual([(p.name, p.price) for p in sq], [('p5', 5)])\n\n        query = (C_Product\n                 .insert({'name': 'p6', 'price': 6})\n                 .returning(C_Product.id, C_Product.name, C_Product.price))\n        cte = query.cte('t')\n        sq = Select((cte,), (cte.c.id, cte.c.name, cte.c.price))\n        res = (C_Archive\n               .insert_from(sq, (sq.c.id, sq.c.name, sq.c.price))\n               .with_cte(cte)\n               .execute())\n        self.assertEqual([(p.name, p.price) for p in C_Archive], [('p6', 6)])\n\n        self.assertEqual(sorted([(p.name, p.price) for p in C_Product]), [\n            ('p0', 0), ('p1', 1), ('p2', 2), ('p3', 3), ('p4', 4), ('p5', 5),\n            ('p6', 6)])\n\n\nclass TestBindTo(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_bind_to(self):\n        for i in (1, 2, 3):\n            user = User.create(username='u%s' % i)\n            Tweet.create(user=user, content='t%s' % i)\n\n        # Alias to a particular field-name.\n        name = Case(User.username, [\n            ('u1', 'user 1'),\n            ('u2', 'user 2')], 'someone else')\n        q = (Tweet\n             .select(Tweet.content, name.alias('username').bind_to(User))\n             .join(User)\n             .order_by(Tweet.content))\n        with self.assertQueryCount(1):\n            self.assertEqual([(t.content, t.user.username) for t in q], [\n                ('t1', 'user 1'),\n                ('t2', 'user 2'),\n                ('t3', 'someone else')])\n\n        # Use a different alias.\n        q = (Tweet\n             .select(Tweet.content, name.alias('display').bind_to(User))\n             .join(User)\n             .order_by(Tweet.content))\n        with self.assertQueryCount(1):\n            self.assertEqual([(t.content, t.user.display) for t in q], [\n                ('t1', 'user 1'),\n                ('t2', 'user 2'),\n                ('t3', 'someone else')])\n\n        # Ensure works with model and field aliases.\n        TA, UA = Tweet.alias(), User.alias()\n        name = Case(UA.username, [\n            ('u1', 'user 1'),\n            ('u2', 'user 2')], 'someone else')\n        q = (TA\n             .select(TA.content, name.alias('display').bind_to(UA))\n             .join(UA, on=(UA.id == TA.user))\n             .order_by(TA.content))\n        with self.assertQueryCount(1):\n            self.assertEqual([(t.content, t.user.display) for t in q], [\n                ('t1', 'user 1'),\n                ('t2', 'user 2'),\n                ('t3', 'someone else')])\n\n\nclass CascadeParent(TestModel):\n    name = TextField()\n\nclass CascadeChild(TestModel):\n    parent = ForeignKeyField(CascadeParent, backref='children',\n                             on_delete='CASCADE')\n    data = TextField()\n\n\nclass TestCascadeDeleteIntegration(ModelTestCase):\n    requires = [CascadeParent, CascadeChild]\n\n    def setUp(self):\n        super(TestCascadeDeleteIntegration, self).setUp()\n        if IS_SQLITE:\n            self.database.pragma('foreign_keys', 1)\n\n    def test_cascade_delete(self):\n        p1 = CascadeParent.create(name='p1')\n        p2 = CascadeParent.create(name='p2')\n        CascadeChild.create(parent=p1, data='c1')\n        CascadeChild.create(parent=p1, data='c2')\n        CascadeChild.create(parent=p2, data='c3')\n\n        self.assertEqual(CascadeChild.select().count(), 3)\n        p1.delete_instance()\n        self.assertEqual(CascadeChild.select().count(), 1)\n        self.assertEqual(CascadeChild.get().data, 'c3')\n"
  },
  {
    "path": "tests/mysql_ext.py",
    "content": "import datetime\n\nfrom peewee import *\nfrom playhouse.mysql_ext import JSONField\nfrom playhouse.mysql_ext import Match\n\nfrom .base import IS_MYSQL_JSON\nfrom .base import ModelDatabaseTestCase\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db_loader\nfrom .base import requires_mysql\nfrom .base import skip_if\nfrom .base import skip_unless\n\n\ntry:\n    import mariadb\nexcept ImportError:\n    mariadb = mariadb_db = None\nelse:\n    mariadb_db = db_loader('mariadb')\ntry:\n    import mysql.connector as mysql_connector\nexcept ImportError:\n    mysql_connector = None\n\nmysql_ext_db = db_loader('mysqlconnector')\n\n\nclass Person(TestModel):\n    first = CharField()\n    last = CharField()\n    dob = DateField(default=datetime.date(2000, 1, 1))\n\n\nclass Note(TestModel):\n    person = ForeignKeyField(Person, backref='notes')\n    content = TextField()\n    timestamp = DateTimeField(default=datetime.datetime.now)\n\n\nclass KJ(TestModel):\n    key = CharField(primary_key=True, max_length=100)\n    data = JSONField()\n\n\n@requires_mysql\n@skip_if(mysql_connector is None, 'mysql-connector not installed')\nclass TestMySQLConnector(ModelTestCase):\n    database = mysql_ext_db\n    requires = [Person, Note]\n\n    def test_basic_operations(self):\n        with self.database.atomic():\n            charlie, huey, zaizee = [Person.create(first=f, last='leifer')\n                                     for f in ('charlie', 'huey', 'zaizee')]\n            # Use nested-transaction.\n            with self.database.atomic():\n                data = (\n                    (charlie, ('foo', 'bar', 'zai')),\n                    (huey, ('meow', 'purr', 'hiss')),\n                    (zaizee, ()))\n                for person, notes in data:\n                    for note in notes:\n                        Note.create(person=person, content=note)\n\n            with self.database.atomic() as sp:\n                Person.create(first='x', last='y')\n                sp.rollback()\n\n        people = Person.select().order_by(Person.first)\n        self.assertEqual([person.first for person in people],\n                         ['charlie', 'huey', 'zaizee'])\n\n        with self.assertQueryCount(1):\n            notes = (Note\n                     .select(Note, Person)\n                     .join(Person)\n                     .order_by(Note.content))\n            self.assertEqual([(n.person.first, n.content) for n in notes], [\n                ('charlie', 'bar'),\n                ('charlie', 'foo'),\n                ('huey', 'hiss'),\n                ('huey', 'meow'),\n                ('huey', 'purr'),\n                ('charlie', 'zai')])\n\n\n@requires_mysql\n@skip_if(mariadb is None, 'mariadb connector not installed')\nclass TestMariaDBConnector(TestMySQLConnector):\n    database = mariadb_db\n\n\n@requires_mysql\n@skip_unless(IS_MYSQL_JSON, 'requires MySQL 5.7+ or 8.x')\nclass TestMySQLJSONField(ModelTestCase):\n    requires = [KJ]\n\n    def test_mysql_json_field(self):\n        values = (\n            0, 1.0, 2.3,\n            True, False,\n            'string',\n            ['foo', 'bar', 'baz'],\n            {'k1': 'v1', 'k2': 'v2'},\n            {'k3': [0, 1.0, 2.3], 'k4': {'x1': 'y1', 'x2': 'y2'}})\n        for i, value in enumerate(values):\n            # Verify data can be written.\n            kj = KJ.create(key='k%s' % i, data=value)\n\n            # Verify value is deserialized correctly.\n            kj_db = KJ['k%s' % i]\n            self.assertEqual(kj_db.data, value)\n\n        kj = KJ.select().where(KJ.data.extract('$.k1') == 'v1').get()\n        self.assertEqual(kj.key, 'k7')\n\n        with self.assertRaises(IntegrityError):\n            KJ.create(key='kx', data=None)\n\n\n@requires_mysql\nclass TestMatchExpression(ModelDatabaseTestCase):\n    requires = [Person]\n\n    def test_match_expression(self):\n        query = (Person\n                 .select()\n                 .where(Match(Person.first, 'charlie')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"first\", \"t1\".\"last\", \"t1\".\"dob\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE MATCH(\"t1\".\"first\") AGAINST(?)'), ['charlie'])\n\n        query = (Person\n                 .select()\n                 .where(Match((Person.first, Person.last), 'huey AND zaizee',\n                              'IN BOOLEAN MODE')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"first\", \"t1\".\"last\", \"t1\".\"dob\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE MATCH(\"t1\".\"first\", \"t1\".\"last\") '\n            'AGAINST(? IN BOOLEAN MODE)'), ['huey AND zaizee'])\n"
  },
  {
    "path": "tests/pool.py",
    "content": "import heapq\nimport os\nimport threading\nimport time\n\nfrom peewee import *\nfrom peewee import _savepoint\nfrom peewee import _transaction\nfrom playhouse.cockroachdb import PooledCockroachDatabase\nfrom playhouse.pool import *\n\nfrom .base import BACKEND\nfrom .base import BaseTestCase\nfrom .base import IS_CRDB\nfrom .base import IS_MYSQL\nfrom .base import IS_POSTGRESQL\nfrom .base import IS_SQLITE\nfrom .base import ModelTestCase\nfrom .base import db_loader\nfrom .base_models import Register\n\n\nclass FakeTransaction(_transaction):\n    def _add_history(self, message):\n        self.db.transaction_history.append(\n            '%s%s' % (message, self._conn))\n\n    def __enter__(self):\n        self._conn = self.db.connection()\n        self._add_history('O')\n        self.db.push_transaction(self)\n\n    def __exit__(self, *args):\n        self._add_history('X')\n        self.db.pop_transaction()\n\n\nclass FakeDatabase(SqliteDatabase):\n    def __init__(self, *args, **kwargs):\n        self.counter = self.closed_counter = kwargs.pop('counter', 0)\n        self.transaction_history = []\n        super(FakeDatabase, self).__init__(*args, **kwargs)\n\n    def _connect(self):\n        self.counter += 1\n        return self.counter\n\n    def _close(self, conn):\n        self.closed_counter += 1\n\n    def transaction(self):\n        return FakeTransaction(self)\n\n\nclass FakePooledDatabase(PooledDatabase, FakeDatabase):\n    def __init__(self, *args, **kwargs):\n        super(FakePooledDatabase, self).__init__(*args, **kwargs)\n        self.conn_key = lambda conn: conn\n\n\nclass PooledTestDatabase(PooledDatabase, SqliteDatabase):\n    pass\n\n\ndef push_conn(db, timestamp, conn):\n    # Push a connection onto the pool heap with a proper monotonic counter.\n    db._heap_counter += 1\n    heapq.heappush(db._connections, (timestamp, db._heap_counter, conn))\n\n\nclass TestPooledDatabase(BaseTestCase):\n    def setUp(self):\n        super(TestPooledDatabase, self).setUp()\n        self.db = FakePooledDatabase('testing')\n\n    def test_connection_pool(self):\n        # Closing and reopening a connection returns us the same conn.\n        self.assertEqual(self.db.connection(), 1)\n        self.assertEqual(self.db.connection(), 1)\n\n        self.db.close()\n        self.db.connect()\n        self.assertEqual(self.db.connection(), 1)\n\n    def test_reuse_connection(self):\n        # Verify the connection pool correctly handles calling connect twice.\n        self.assertEqual(self.db.connection(), 1)\n        self.assertRaises(OperationalError, self.db.connect)\n        self.assertFalse(self.db.connect(reuse_if_open=True))\n\n        self.assertEqual(self.db.connection(), 1)\n        self.db.close()\n        self.db.connect()\n        self.assertEqual(self.db.connection(), 1)\n\n    def test_concurrent_connections(self):\n        db = FakePooledDatabase('testing')\n        barrier = threading.Barrier(6)  # 5 workers + main thread.\n\n        def open_conn():\n            db.connect()\n            barrier.wait(timeout=2)\n            db.close()\n\n        # Simulate 5 concurrent connections.\n        threads = [threading.Thread(target=open_conn) for i in range(5)]\n        for thread in threads:\n            thread.start()\n\n        # Wait for all connections to be opened, then release to run.\n        barrier.wait(timeout=2)\n        for t in threads: t.join()\n\n        self.assertEqual(db.counter, 5)\n        self.assertEqual(\n            sorted([conn for _, _, conn in db._connections]),\n            [1, 2, 3, 4, 5])  # All 5 are ready to be re-used.\n        self.assertEqual(db._in_use, {})\n\n    def test_max_conns(self):\n        for i in range(self.db._max_connections):\n            self.db._state.closed = True  # Hack to make it appear closed.\n            self.db.connect()\n            self.assertEqual(self.db.connection(), i + 1)\n        self.db._state.closed = True\n        self.assertRaises(ValueError, self.db.connect)\n\n    def test_stale_timeout(self):\n        # Create a test database with a very short stale timeout.\n        db = FakePooledDatabase('testing', stale_timeout=.001)\n        self.assertEqual(db.connection(), 1)\n        self.assertTrue(1 in db._in_use)\n\n        # Sleep long enough for the connection to be considered stale.\n        time.sleep(.001)\n\n        # When we close, since the conn is stale it won't be returned to\n        # the pool.\n        db.close()\n        self.assertEqual(db._in_use, {})\n        self.assertEqual(db._connections, [])\n\n        # A new connection will be returned.\n        self.assertEqual(db.connection(), 2)\n\n    def test_stale_on_checkout(self):\n        # Create a test database with a very short stale timeout.\n        db = FakePooledDatabase('testing', stale_timeout=1)\n        self.assertEqual(db.connection(), 1)\n        self.assertTrue(1 in db._in_use)\n\n        # When we close, the conn should not be stale so it won't return to\n        # the pool.\n        db.close()\n\n        # Sleep long enough for the connection to be considered stale.\n        self.assertEqual(db._in_use, {})\n        self.assertEqual(len(db._connections), 1)\n\n        _, hc, conn = db._connections[0]\n        db._connections[0] = (time.time() - 2, hc, conn)\n\n        # A new connection will be returned, as the original one is stale.\n        # The stale connection (1) will be removed.\n        self.assertEqual(db.connection(), 2)\n\n    def test_manual_close(self):\n        self.assertEqual(self.db.connection(), 1)\n        self.db.manual_close()\n\n        # When we manually close a connection that's not yet stale, we add it\n        # back to the queue (because close() calls _close()), then close it\n        # for real, and mark it with a tombstone. The next time it's checked\n        # out, it will simply be removed and skipped over.\n        self.assertEqual(len(self.db._connections), 0)\n        self.assertEqual(self.db._in_use, {})\n\n        self.assertEqual(self.db.connection(), 2)\n        self.assertEqual(len(self.db._connections), 0)\n        self.assertEqual(list(self.db._in_use.keys()), [2])\n\n        self.db.close()\n        self.assertEqual(self.db.connection(), 2)\n\n    def test_close_idle(self):\n        db = FakePooledDatabase('testing', counter=3)\n\n        now = time.time()\n        now = time.time()\n        push_conn(db, now - 10, 3)\n        push_conn(db, now - 5, 2)\n        push_conn(db, now - 1, 1)\n\n        self.assertEqual(db.connection(), 3)\n        self.assertTrue(3 in db._in_use)\n\n        db.close_idle()\n        self.assertEqual(len(db._connections), 0)\n        self.assertEqual(len(db._in_use), 1)\n        self.assertTrue(3 in db._in_use)\n        self.assertEqual(db.connection(), 3)\n\n        db.manual_close()\n        self.assertEqual(db.connection(), 4)\n\n    def test_close_stale(self):\n        db = FakePooledDatabase('testing', counter=3)\n\n        now = time.time()\n        # Closing stale uses the last checkout time rather than the creation\n        # time for the connection.\n        db._in_use[1] = PoolConnection(now - 400, 1, now - 300)\n        db._in_use[2] = PoolConnection(now - 200, 2, now - 200)\n        db._in_use[3] = PoolConnection(now - 300, 3, now - 100)\n        db._in_use[4] = PoolConnection(now, 4, now)\n        self.assertEqual(db.close_stale(age=200), 2)\n        self.assertEqual(len(db._in_use), 2)\n        self.assertEqual(sorted(db._in_use), [3, 4])\n\n    def test_close_all(self):\n        db = FakePooledDatabase('testing', counter=3)\n\n        now = time.time()\n        push_conn(db, now - 10, 3)\n        push_conn(db, now - 5, 2)\n        push_conn(db, now - 1, 1)\n        self.assertEqual(db.connection(), 3)\n        self.assertTrue(3 in db._in_use)\n\n        db.close_all()\n        self.assertEqual(len(db._connections), 0)\n        self.assertEqual(len(db._in_use), 0)\n\n        self.assertEqual(db.connection(), 4)\n\n    def test_stale_timeout_cascade(self):\n        now = time.time()\n        db = FakePooledDatabase('testing', stale_timeout=10)\n        conns = [\n            (now - 20, 1),\n            (now - 15, 2),\n            (now - 5, 3),\n            (now, 4),\n        ]\n        for ts, conn in conns:\n            push_conn(db, ts, conn)\n\n        self.assertEqual(db.connection(), 3)\n        self.assertEqual(len(db._in_use), 1)\n        self.assertTrue(3 in db._in_use)\n        self.assertEqual(len(db._connections), 1)\n        self.assertEqual(db._connections[0][2], 4)\n\n    def test_connect_cascade(self):\n        now = time.time()\n        class ClosedPooledDatabase(FakePooledDatabase):\n            def _is_closed(self, conn):\n                return conn in (2, 4)\n\n        db = ClosedPooledDatabase('testing', stale_timeout=10)\n\n        conns = [\n            (now - 15, 1),  # Skipped due to being stale.\n            (now - 5, 2),  # Will appear closed.\n            (now - 3, 3),\n            (now, 4),  # Will appear closed.\n        ]\n        db.counter = 4  # The next connection we create will have id=5.\n        for ts, conn in conns:\n            push_conn(db, ts, conn)\n\n        # Conn 3 is not stale or closed, so we will get it.\n        self.assertEqual(db.connection(), 3)\n        self.assertEqual(len(db._in_use), 1)\n        self.assertTrue(3 in db._in_use)\n        pool_conn = db._in_use[3]\n        self.assertEqual(pool_conn.timestamp, now - 3)\n        self.assertEqual(pool_conn.connection, 3)\n\n        # Only conn 4 remains in the idle pool.\n        self.assertEqual(len(db._connections), 1)\n        self.assertEqual(db._connections[0][2], 4)\n\n        # Since conn 4 is closed, we will open a new conn.\n        db._state.closed = True  # Pretend we're in a different thread.\n        db.connect()\n        self.assertEqual(db.connection(), 5)\n        self.assertEqual(sorted(db._in_use.keys()), [3, 5])\n        self.assertEqual(db._connections, [])\n\n    def test_db_context(self):\n        self.assertEqual(self.db.connection(), 1)\n        with self.db:\n            self.assertEqual(self.db.connection(), 1)\n            self.assertEqual(self.db.transaction_history, ['O1'])\n\n        self.assertEqual(self.db.connection(), 1)\n        self.assertEqual(self.db.transaction_history, ['O1', 'X1'])\n\n        with self.db:\n            self.assertEqual(self.db.connection(), 1)\n\n        self.assertEqual(len(self.db._connections), 1)\n        self.assertEqual(len(self.db._in_use), 0)\n\n    def test_db_context_threads(self):\n        barrier = threading.Barrier(6)  # 5 workers + main thread.\n        def create_context():\n            with self.db:\n                barrier.wait(timeout=2)\n\n        threads = [threading.Thread(target=create_context) for i in range(5)]\n        for thread in threads: thread.start()\n\n        barrier.wait(timeout=2)\n        for thread in threads: thread.join()\n\n        self.assertEqual(self.db.counter, 5)\n        self.assertEqual(len(self.db._connections), 5)\n        self.assertEqual(len(self.db._in_use), 0)\n\n    def test_heap_counter_deterministic_ordering(self):\n        # Verify that connections pushed with the same timestamp are returned\n        # in order.\n        now = time.time()\n        push_conn(self.db, now, 'a')\n        push_conn(self.db, now, 'b')\n        push_conn(self.db, now, 'c')\n\n        results = []\n        while self.db._connections:\n            ts, counter, conn = heapq.heappop(self.db._connections)\n            results.append(conn)\n        self.assertEqual(results, ['a', 'b', 'c'])\n\n    def test_close_conn_removes_from_in_use(self):\n        # _close(conn, close_conn=True) should pop the key from _in_use AND\n        # close the underlying driver conn.\n        self.assertEqual(self.db.connection(), 1)\n        self.assertTrue(1 in self.db._in_use)\n\n        closed_before = self.db.closed_counter\n        self.db._close(1, close_conn=True)\n\n        self.assertNotIn(1, self.db._in_use)\n        self.assertEqual(self.db.closed_counter, closed_before + 1)\n\n    def test_double_close_is_noop(self):\n        # Calling _close on a connection not in _in_use (and close_conn=False)\n        # should be a safe no-op rather than raising or leaking.\n        self.assertEqual(self.db.connection(), 1)\n        self.db.close()  # Returns conn 1 to the pool.\n\n        self.assertNotIn(1, self.db._in_use)\n        closed_before = self.db.closed_counter\n        # Second close should do nothing.\n        self.db._close(1)\n        self.assertEqual(self.db.closed_counter, closed_before)\n        # Pool state unchanged.\n        self.assertEqual(len(self.db._connections), 1)\n\n    def test_can_reuse_false_closes_connection(self):\n        # When _can_reuse returns False on check-in, the connection should be\n        # closed at the driver level and not returned to the pool.\n        class NotReusablePooledDatabase(FakePooledDatabase):\n            def _can_reuse(self, conn):\n                return False\n\n        db = NotReusablePooledDatabase('testing')\n        self.assertEqual(db.connection(), 1)\n        closed_before = db.closed_counter\n\n        db.close()\n\n        # Connection should have been driver-closed, not pooled.\n        self.assertEqual(db.closed_counter, closed_before + 1)\n        self.assertEqual(len(db._connections), 0)\n        self.assertEqual(db._in_use, {})\n\n        # Next connect creates a brand new connection.\n        self.assertEqual(db.connection(), 2)\n\n    def test_close_raw_swallows_exception(self):\n        called = []\n        # _close_raw should not propagate exceptions from the driver.\n        class BrokenDriverClose(FakeDatabase):\n            def _close(self, conn):\n                called.append(conn)\n                raise RuntimeError('failed')\n\n        class BrokenPool(FakePooledDatabase, BrokenDriverClose):\n            pass\n\n        db = BrokenPool('testing')\n        db._close_raw(1337)\n        self.assertEqual(called, [1337])\n\n    def test_close_stale_removes_from_in_use(self):\n        # Verify that close_stale both driver-closes the connection AND\n        # removes it from _in_use (no dangling keys).\n        db = FakePooledDatabase('testing', counter=2)\n\n        now = time.time()\n        db._in_use[1] = PoolConnection(now - 1000, 1, now - 1000)\n        db._in_use[2] = PoolConnection(now, 2, now)\n\n        closed_before = db.closed_counter\n        self.assertEqual(db.close_stale(age=500), 1)\n        self.assertNotIn(1, db._in_use)\n        self.assertIn(2, db._in_use)\n        self.assertEqual(db.closed_counter, closed_before + 1)\n\n    def test_close_all_clears_both_pools(self):\n        # close_all should leave both _connections and _in_use completely\n        # empty, and driver-close every connection.\n        db = FakePooledDatabase('testing', counter=3)\n\n        now = time.time()\n        push_conn(db, now - 5, 1)\n        push_conn(db, now - 1, 2)\n\n        # Simulate two in-use connections.\n        db._in_use[3] = PoolConnection(now, 3, now)\n        db._in_use[4] = PoolConnection(now, 4, now)\n\n        # One more for the \"current thread\" via normal connect path so\n        # self.close() inside close_all has something to reset.\n        db._state.closed = True\n        db.connect()\n        conn = db.connection()\n        self.assertIn(db.conn_key(conn), db._in_use)\n\n        closed_before = db.closed_counter\n        db.close_all()\n\n        self.assertEqual(db._connections, [])\n        self.assertEqual(db._in_use, {})\n        # 2 idle + 2 manually-added in_use + the current thread's conn = 5.\n        # (close_all calls self.close() which triggers _close for the current\n        # thread's conn, but that goes through the return-to-pool path, not\n        # _close_raw.  The subsequent loop over the snapshot handles it.)\n        self.assertGreaterEqual(db.closed_counter, closed_before + 4)\n\n    def test_connect_timeout_with_condition_variable(self):\n        # Verify that connect() with a timeout raises after the timeout\n        # expires when the pool is exhausted.\n        db = FakePooledDatabase('testing', max_connections=1, timeout=0.15)\n        self.assertEqual(db.connection(), 1)\n\n        errors = []\n        def try_connect():\n            db._state.closed = True  # Appear as a new thread.\n            try:\n                db.connect()\n            except MaxConnectionsExceeded:\n                errors.append(True)\n\n        t = threading.Thread(target=try_connect)\n        start = time.monotonic()\n        t.start()\n        t.join(timeout=2)\n        elapsed = time.monotonic() - start\n\n        # Should have waited roughly the timeout duration.\n        self.assertEqual(len(errors), 1)\n        self.assertGreaterEqual(elapsed, 0.1)\n\n    def test_connect_timeout_wakes_on_return(self):\n        # Verify that a waiting thread unblocks promptly when a connection\n        # is returned to the pool (via the Condition variable notify).\n        db = FakePooledDatabase('testing', max_connections=1, timeout=5)\n        self.assertEqual(db.connection(), 1)\n\n        results = []\n        def try_connect():\n            db._state.closed = True\n            try:\n                db.connect()\n                results.append(db.connection())\n            except MaxConnectionsExceeded:\n                results.append('timeout')\n\n        t = threading.Thread(target=try_connect)\n        t.start()\n\n        # Give the thread a moment to start waiting.\n        time.sleep(0.05)\n\n        # Return conn 1 to the pool — should wake the waiting thread.\n        db.close()\n\n        t.join(timeout=2)\n        self.assertFalse(t.is_alive(), 'Thread did not wake up.')\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0], 1)  # Got the recycled connection.\n\n    def test_connect_timeout_zero_becomes_infinite(self):\n        # A timeout of 0 should be treated as infinite (no immediate failure).\n        db = FakePooledDatabase('testing', max_connections=1, timeout=0)\n        self.assertEqual(db._wait_timeout, float('inf'))\n\n    def test_close_all_wakes_waiters(self):\n        # Threads blocked in connect() should be woken by close_all() so they\n        # can create fresh connections.\n        db = FakePooledDatabase('testing', max_connections=1, timeout=5)\n        self.assertEqual(db.connection(), 1)\n\n        results = []\n        def try_connect():\n            db._state.closed = True\n            try:\n                db.connect()\n                results.append(db.connection())\n            except MaxConnectionsExceeded:\n                results.append('timeout')\n\n        t = threading.Thread(target=try_connect)\n        t.start()\n        time.sleep(0.05)\n\n        # close_all frees the slot and calls notify_all.\n        db.close_all()\n\n        t.join(timeout=2)\n        self.assertFalse(t.is_alive(), 'Thread was not woken by close_all.')\n        self.assertEqual(len(results), 1)\n        # After close_all, the thread should have gotten a fresh connection.\n        self.assertEqual(results[0], 2)\n\n    def test_close_stale_iteration(self):\n        db = FakePooledDatabase('testing', counter=10)\n        now = time.time()\n        for i in range(1, 11):\n            db._in_use[i] = PoolConnection(now - 1000, i, now - 1000)\n\n        # All 10 should be closed.\n        self.assertEqual(db.close_stale(age=500), 10)\n        self.assertEqual(db._in_use, {})\n\n    def test_concurrent_close_stale_and_return(self):\n        # Exercise close_stale running while other threads are actively\n        # returning connections (calling close()).  The snapshot-before-mutate\n        # pattern and the RLock should keep everything consistent.\n        db = FakePooledDatabase('testing', max_connections=20)\n        barrier = threading.Barrier(11)  # 10 workers + main thread.\n        errors = []\n\n        def worker(n):\n            \"\"\"Check out a connection, wait for all workers to be ready,\n            then return it.\"\"\"\n            try:\n                db._state.closed = True\n                db.connect()\n                barrier.wait(timeout=2)  # Let all workers connect.\n                barrier.wait(timeout=2)  # Let main() finish w/timestamps.\n                # Small stagger so close_stale and close() overlap.\n                time.sleep(0.001 * (n % 3))\n                db.close()\n            except Exception as exc:\n                errors.append(exc)\n\n        # Spin up 10 threads that each grab and return a connection.\n        threads = [threading.Thread(target=worker, args=(i,))\n                   for i in range(10)]\n        for t in threads: t.start()\n\n        # Wait until all threads hold a connection.\n        barrier.wait(timeout=2)\n\n        # Artificially back-date half the checked_out times so that\n        # close_stale will try to close them while threads are returning.\n        now = time.time()\n        for i, key in enumerate(list(db._in_use)):\n            if i % 2 == 0:\n                pc = db._in_use[key]\n                db._in_use[key] = PoolConnection(pc.timestamp, pc.connection,\n                                                 now - 10000)\n\n        # Release the barrier so threads start returning connections, and\n        # simultaneously run close_stale from the main thread.\n        barrier.wait(timeout=2)\n        closed = db.close_stale(age=5000)\n        for t in threads: t.join(timeout=2)\n\n        self.assertEqual(errors, [])\n        for key in db._in_use:\n            for _, _, conn in db._connections:\n                self.assertNotEqual(db.conn_key(conn), key)\n\n    def test_manual_close_when_already_closed(self):\n        # manual_close on an already-closed database should return False.\n        self.assertFalse(self.db.manual_close())  # Never opened.\n\n        self.db.connect()\n        self.db.close()\n        self.assertFalse(self.db.manual_close())  # Already closed.\n\n    def test_close_idle_driver_closes_all(self):\n        # Every idle connection should be driver-closed.\n        db = FakePooledDatabase('testing', counter=5)\n        now = time.time()\n        for i in range(1, 6):\n            push_conn(db, now - i, i)\n\n        closed_before = db.closed_counter\n        db.close_idle()\n        self.assertEqual(db._connections, [])\n        self.assertEqual(db.closed_counter, closed_before + 5)\n\n    def test_max_connections_zero_means_unlimited(self):\n        # max_connections=0 (falsy) should mean no limit.\n        db = FakePooledDatabase('testing', max_connections=0)\n        for i in range(50):\n            db._state.closed = True\n            db.connect()\n        self.assertEqual(len(db._in_use), 50)\n\n    def test_stale_and_closed_all_skipped(self):\n        # If every connection in the pool is either stale or closed, a new one\n        # should be created.\n        class AllClosedDatabase(FakePooledDatabase):\n            def _is_closed(self, conn):\n                return True\n\n        db = AllClosedDatabase('testing', stale_timeout=10)\n        now = time.time()\n        push_conn(db, now - 20, 1)  # Stale.\n        push_conn(db, now, 2)       # Closed (per _is_closed override).\n        db.counter = 2\n\n        self.assertEqual(db.connection(), 3)\n        self.assertEqual(db._connections, [])\n        self.assertEqual(list(db._in_use.keys()), [3])\n\n    def test_init_updates_pool_parameters(self):\n        # The init() method should allow updating pool parameters after\n        # initial construction.\n        db = FakePooledDatabase('testing', max_connections=5, stale_timeout=10,\n                                timeout=2)\n        self.assertEqual(db._max_connections, 5)\n        self.assertEqual(db._stale_timeout, 10)\n        self.assertEqual(db._wait_timeout, 2)\n\n        db.init('testing', max_connections=50, stale_timeout=100, timeout=20)\n        self.assertEqual(db._max_connections, 50)\n        self.assertEqual(db._stale_timeout, 100)\n        self.assertEqual(db._wait_timeout, 20)\n\n    def test_init_timeout_zero_becomes_infinite(self):\n        db = FakePooledDatabase('testing', timeout=5)\n        self.assertEqual(db._wait_timeout, 5)\n\n        db.init('testing', timeout=0)\n        self.assertEqual(db._wait_timeout, float('inf'))\n\n\nclass TestLivePooledDatabase(ModelTestCase):\n    database = PooledTestDatabase('test_pooled.db')\n    requires = [Register]\n\n    def tearDown(self):\n        super(TestLivePooledDatabase, self).tearDown()\n        self.database.close_idle()\n        if os.path.exists('test_pooled.db'):\n            os.unlink('test_pooled.db')\n\n    def test_reuse_connection(self):\n        for i in range(5):\n            Register.create(value=i)\n        conn_id = id(self.database.connection())\n        self.database.close()\n\n        for i in range(5, 10):\n            Register.create(value=i)\n        self.assertEqual(id(self.database.connection()), conn_id)\n        self.assertEqual(\n            [x.value for x in Register.select().order_by(Register.id)],\n            list(range(10)))\n\n    def test_db_context(self):\n        with self.database:\n            Register.create(value=1)\n            with self.database.atomic() as sp:\n                self.assertTrue(isinstance(sp, _savepoint))\n                Register.create(value=2)\n                sp.rollback()\n\n            with self.database.atomic() as sp:\n                self.assertTrue(isinstance(sp, _savepoint))\n                Register.create(value=3)\n\n        with self.database:\n            values = [r.value for r in Register.select().order_by(Register.id)]\n            self.assertEqual(values, [1, 3])\n\n    def test_bad_connection(self):\n        self.database.connection()\n        try:\n            self.database.execute_sql('select 1/0')\n        except Exception as exc:\n            pass\n        self.database.close()\n        self.database.connect()\n\n\nclass TestPooledDatabaseIntegration(ModelTestCase):\n    requires = [Register]\n\n    def setUp(self):\n        params = {}\n        if IS_MYSQL:\n            db_class = PooledMySQLDatabase\n        elif IS_POSTGRESQL:\n            db_class = PooledPostgresqlDatabase\n        elif IS_CRDB:\n            db_class = PooledCockroachDatabase\n        else:\n            db_class = PooledSqliteDatabase\n            params['check_same_thread'] = False\n        self.database = db_loader(BACKEND, db_class=db_class, **params)\n        super(TestPooledDatabaseIntegration, self).setUp()\n\n    def assertConnections(self, expected):\n        available = len(self.database._connections)\n        in_use = len(self.database._in_use)\n        self.assertEqual(available + in_use, expected,\n                         'expected %s, got: %s available, %s in use'\n                         % (expected, available, in_use))\n\n    def test_pooled_database_integration(self):\n        # Connection should be open from the setup method.\n        self.assertFalse(self.database.is_closed())\n        self.assertConnections(1)\n        self.assertTrue(self.database.close())\n        self.assertTrue(self.database.is_closed())\n        self.assertConnections(1)\n\n        barrier = threading.Barrier(5)  # 4 workers + main thread.\n        def connect():\n            self.assertTrue(self.database.is_closed())\n            self.assertTrue(self.database.connect())\n            self.assertFalse(self.database.is_closed())\n            barrier.wait(timeout=2)\n            self.assertTrue(self.database.close())\n            self.assertTrue(self.database.is_closed())\n\n        # Open connections in 4 separate threads.\n        threads = [threading.Thread(target=connect) for _ in range(4)]\n        for t in threads: t.start()\n\n        # Close connections in all 4 threads.\n        barrier.wait(timeout=2)\n        for t in threads: t.join()\n\n        # Verify that there are 4 connections available in the pool.\n        self.assertConnections(4)\n        self.assertEqual(len(self.database._connections), 4)  # Available.\n        self.assertEqual(len(self.database._in_use), 0)\n\n        # Verify state of the main thread, just a sanity check.\n        self.assertTrue(self.database.is_closed())\n\n        # Opening a connection will pull from the pool.\n        self.assertTrue(self.database.connect())\n        self.assertFalse(self.database.connect(reuse_if_open=True))\n        self.assertConnections(4)\n        self.assertEqual(len(self.database._in_use), 1)\n\n        # Calling close_all() closes everything, including calling thread.\n        self.database.close_all()\n        self.assertConnections(0)\n        self.assertTrue(self.database.is_closed())\n\n    def test_pool_with_models(self):\n        self.database.close()\n        barrier = threading.Barrier(5)  # 4 workers + main thread.\n\n        def create_obj(i):\n            with self.database.connection_context():\n                with self.database.atomic():\n                    Register.create(value=i)\n                barrier.wait(timeout=2)\n\n        # Create 4 objects, one in each thread. The INSERT will be wrapped in a\n        # transaction, and after COMMIT (but while the conn is still open), we\n        # will wait for the signal that all objects were created. This ensures\n        # that all our connections are open concurrently.\n        threads = [threading.Thread(target=create_obj, args=(i,))\n                   for i in range(4)]\n        for t in threads: t.start()\n\n        # Signal threads that they can exit now and ensure all exited.\n        self.database.connect()\n        barrier.wait(timeout=2)\n        for t in threads: t.join()\n\n        # Close connection from main thread as well.\n        self.database.close()\n\n        self.assertConnections(5)\n        self.assertEqual(len(self.database._in_use), 0)\n\n        # Cycle through the available connections, running a query on each, and\n        # then manually closing it.\n        for i in range(5):\n            self.assertTrue(self.database.is_closed())\n            self.assertTrue(self.database.connect())\n\n            # Sanity check to verify objects are created.\n            query = Register.select().order_by(Register.value)\n            self.assertEqual([r.value for r in query], [0, 1, 2, 3])\n            self.database.manual_close()\n            self.assertConnections(4 - i)\n\n        self.assertConnections(0)\n        self.assertEqual(len(self.database._in_use), 0)\n"
  },
  {
    "path": "tests/postgres.py",
    "content": "#coding:utf-8\nimport datetime\nimport functools\nimport json\nimport os\nimport uuid\nfrom decimal import Decimal as Dc\nfrom types import MethodType\n\nfrom peewee import *\nfrom playhouse.postgres_ext import *\nfrom playhouse.reflection import Introspector\n\nfrom .base import BaseTestCase\nfrom .base import DatabaseTestCase\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db_loader\nfrom .base import requires_models\nfrom .base import skip_if\nfrom .base import skip_unless\nfrom .base_models import Register\nfrom .base_models import Tweet\nfrom .base_models import User\nfrom .postgres_helpers import BaseBinaryJsonFieldTestCase\nfrom .postgres_helpers import BaseJsonFieldTestCase\n\n\ndb = db_loader('postgres', db_class=PostgresqlExtDatabase)\n\n\nclass HStoreModel(TestModel):\n    name = CharField()\n    data = HStoreField()\nD = HStoreModel.data\n\n\nclass ArrayModel(TestModel):\n    tags = ArrayField(CharField)\n    ints = ArrayField(IntegerField, dimensions=2)\n\n\nclass UUIDList(TestModel):\n    key = CharField()\n    id_list = ArrayField(BinaryUUIDField, convert_values=True, index=False)\n    id_list_native = ArrayField(UUIDField, index=False)\n\n\nclass ArrayTSModel(TestModel):\n    key = CharField(max_length=100, primary_key=True)\n    timestamps = ArrayField(TimestampField, convert_values=True)\n\n\nclass DecimalArray(TestModel):\n    values = ArrayField(DecimalField, field_kwargs={'decimal_places': 1})\n\n\nclass FTSModel(TestModel):\n    title = CharField()\n    data = TextField()\n    fts_data = TSVectorField()\n\n\nclass JsonModel(TestModel):\n    data = JSONField()\n\nclass JsonModelNull(TestModel):\n    data = JSONField(null=True)\n\nclass BJson(TestModel):\n    data = BinaryJSONField()\n\nclass JData(TestModel):\n    d1 = BinaryJSONField()\n    d2 = BinaryJSONField(index=False)\n\n\nclass UUIDEncoder(json.JSONEncoder):\n    def default(self, obj):\n        if isinstance(obj, uuid.UUID):\n            return str(obj)\n        return super(UUIDEncoder, self).default(obj)\n\ndef dumps(obj):\n    return json.dumps(obj, cls=UUIDEncoder)\n\n\nclass CustomJSONDumps(TestModel):\n    jf = JSONField(dumps=dumps)\n    jbf = BinaryJSONField(dumps=dumps)\n\n\nclass Normal(TestModel):\n    data = TextField()\n\n\nclass Event(TestModel):\n    name = CharField()\n    duration = IntervalField()\n\n\nclass TZModel(TestModel):\n    dt = DateTimeTZField()\n\n\nclass TestTZField(ModelTestCase):\n    database = db\n    requires = [TZModel]\n\n    @skip_if(os.environ.get('CI'), 'running in ci mode, skipping')\n    def test_tz_field(self):\n        self.database.set_time_zone('us/eastern')\n\n        # Our naive datetime is treated as if it were in US/Eastern.\n        dt = datetime.datetime(2019, 1, 1, 12)\n        tz = TZModel.create(dt=dt)\n        self.assertTrue(tz.dt.tzinfo is None)\n\n        # When we retrieve the row, psycopg will attach the appropriate tzinfo\n        # data. The value is returned as an \"aware\" datetime in US/Eastern.\n        tz_db = TZModel[tz.id]\n        self.assertTrue(tz_db.dt.tzinfo is not None)\n        self.assertEqual(tz_db.dt.timetuple()[:4], (2019, 1, 1, 12))\n        self.assertEqual(tz_db.dt.utctimetuple()[:4], (2019, 1, 1, 17))\n\n        class _UTC(datetime.tzinfo):\n            def utcoffset(self, dt): return datetime.timedelta(0)\n            def tzname(self, dt): return \"UTC\"\n            def dst(self, dt): return datetime.timedelta(0)\n        UTC = _UTC()\n\n        # We can explicitly insert a row with a different timezone, however.\n        # When we read the row back, it is returned in US/Eastern.\n        dt2 = datetime.datetime(2019, 1, 1, 12, tzinfo=UTC)\n        tz2 = TZModel.create(dt=dt2)\n        tz2_db = TZModel[tz2.id]\n        self.assertEqual(tz2_db.dt.timetuple()[:4], (2019, 1, 1, 7))\n        self.assertEqual(tz2_db.dt.utctimetuple()[:4], (2019, 1, 1, 12))\n\n        # Querying using naive datetime, treated as localtime (US/Eastern).\n        tzq1 = TZModel.get(TZModel.dt == dt)\n        self.assertEqual(tzq1.id, tz.id)\n\n        # Querying using aware datetime, tzinfo is respected.\n        tzq2 = TZModel.get(TZModel.dt == dt2)\n        self.assertEqual(tzq2.id, tz2.id)\n\n        # Change the connection timezone?\n        self.database.set_time_zone('us/central')\n        tz_db = TZModel[tz.id]\n        self.assertEqual(tz_db.dt.timetuple()[:4], (2019, 1, 1, 11))\n        self.assertEqual(tz_db.dt.utctimetuple()[:4], (2019, 1, 1, 17))\n\n        tz2_db = TZModel[tz2.id]\n        self.assertEqual(tz2_db.dt.timetuple()[:4], (2019, 1, 1, 6))\n        self.assertEqual(tz2_db.dt.utctimetuple()[:4], (2019, 1, 1, 12))\n\n\nclass TestHStoreField(ModelTestCase):\n    database = db_loader('postgres', db_class=PostgresqlExtDatabase,\n                         register_hstore=True)\n    requires = [HStoreModel]\n\n    def setUp(self):\n        super(TestHStoreField, self).setUp()\n        self.t1 = HStoreModel.create(name='t1', data={'k1': 'v1', 'k2': 'v2'})\n        self.t2 = HStoreModel.create(name='t2', data={'k2': 'v2', 'k3': 'v3'})\n\n    def by_name(self, name):\n        return HStoreModel.get(HStoreModel.name == name).data\n\n    def test_hstore_storage(self):\n        self.assertEqual(self.by_name('t1'), {'k1': 'v1', 'k2': 'v2'})\n        self.assertEqual(self.by_name('t2'), {'k2': 'v2', 'k3': 'v3'})\n\n        self.t1.data = {'k4': 'v4'}\n        self.t1.save()\n        self.assertEqual(self.by_name('t1'), {'k4': 'v4'})\n\n        HStoreModel.create(name='t3', data={})\n        self.assertEqual(self.by_name('t3'), {})\n\n    def query(self, *cols):\n        return (HStoreModel\n                .select(HStoreModel.name, *cols)\n                .order_by(HStoreModel.id))\n\n    def test_hstore_selecting(self):\n        query = self.query(D.keys().alias('keys'))\n        self.assertEqual([(x.name, sorted(x.keys)) for x in query], [\n            ('t1', ['k1', 'k2']), ('t2', ['k2', 'k3'])])\n\n        query = self.query(D.values().alias('vals'))\n        self.assertEqual([(x.name, sorted(x.vals)) for x in query], [\n            ('t1', ['v1', 'v2']), ('t2', ['v2', 'v3'])])\n\n        query = self.query(D.items().alias('mtx'))\n        self.assertEqual([(x.name, sorted(x.mtx)) for x in query], [\n            ('t1', [['k1', 'v1'], ['k2', 'v2']]),\n            ('t2', [['k2', 'v2'], ['k3', 'v3']])])\n\n        query = self.query(D.slice('k2', 'k3').alias('kz'))\n        self.assertEqual([(x.name, x.kz) for x in query], [\n            ('t1', {'k2': 'v2'}),\n            ('t2', {'k2': 'v2', 'k3': 'v3'})])\n\n        query = self.query(D.slice('k4').alias('kz'))\n        self.assertEqual([(x.name, x.kz) for x in query], [\n            ('t1', {}), ('t2', {})])\n\n        query = self.query(D.exists('k3').alias('ke'))\n        self.assertEqual([(x.name, x.ke) for x in query], [\n            ('t1', False), ('t2', True)])\n\n        query = self.query(D.defined('k3').alias('ke'))\n        self.assertEqual([(x.name, x.ke) for x in query], [\n            ('t1', False), ('t2', True)])\n\n        query = self.query(D['k1'].alias('k1'))\n        self.assertEqual([(x.name, x.k1) for x in query], [\n            ('t1', 'v1'), ('t2', None)])\n\n        query = self.query().where(D['k1'] == 'v1')\n        self.assertEqual([x.name for x in query], ['t1'])\n\n    def assertWhere(self, expr, names):\n        query = HStoreModel.select().where(expr)\n        self.assertEqual([x.name for x in query], names)\n\n    def test_hstore_filtering(self):\n        self.assertWhere(D == {'k1': 'v1', 'k2': 'v2'}, ['t1'])\n        self.assertWhere(D == {'k2': 'v2'}, [])\n\n        self.assertWhere(D.contains('k3'), ['t2'])\n        self.assertWhere(D.contains(['k2', 'k3']), ['t2'])\n        self.assertWhere(D.contains(['k2']), ['t1', 't2'])\n\n        # test dict\n        self.assertWhere(D.contains({'k2': 'v2', 'k3': 'v3'}), ['t2'])\n        self.assertWhere(D.contains({'k2': 'v2'}), ['t1', 't2'])\n        self.assertWhere(D.contains({'k2': 'v3'}), [])\n\n        # test contains any.\n        self.assertWhere(D.contains_any('k3', 'kx'), ['t2'])\n        self.assertWhere(D.contains_any('k2', 'x', 'k3'), ['t1', 't2'])\n        self.assertWhere(D.contains_any('x', 'kx', 'y'), [])\n\n    def test_hstore_filter_functions(self):\n        self.assertWhere(D.exists('k2') == True, ['t1', 't2'])\n        self.assertWhere(D.exists('k3') == True, ['t2'])\n        self.assertWhere(D.defined('k2') == True, ['t1', 't2'])\n        self.assertWhere(D.defined('k3') == True, ['t2'])\n\n    def test_hstore_update(self):\n        rc = (HStoreModel\n              .update(data=D.update(k4='v4'))\n              .where(HStoreModel.name == 't1')\n              .execute())\n        self.assertTrue(rc > 0)\n\n        self.assertEqual(self.by_name('t1'),\n                         {'k1': 'v1', 'k2': 'v2', 'k4': 'v4'})\n\n        rc = (HStoreModel\n              .update(data=D.update(k5='v5', k6='v6'))\n              .where(HStoreModel.name == 't2')\n              .execute())\n        self.assertTrue(rc > 0)\n\n        self.assertEqual(self.by_name('t2'),\n                         {'k2': 'v2', 'k3': 'v3', 'k5': 'v5', 'k6': 'v6'})\n\n        HStoreModel.update(data=D.update(k2='vxxx')).execute()\n        self.assertEqual([x.data for x in self.query(D)], [\n            {'k1': 'v1', 'k2': 'vxxx', 'k4': 'v4'},\n            {'k2': 'vxxx', 'k3': 'v3', 'k5': 'v5', 'k6': 'v6'}])\n\n        (HStoreModel\n         .update(data=D.delete('k4'))\n         .where(HStoreModel.name == 't1')\n         .execute())\n\n        self.assertEqual(self.by_name('t1'), {'k1': 'v1', 'k2': 'vxxx'})\n\n        HStoreModel.update(data=D.delete('k5')).execute()\n        self.assertEqual([x.data for x in self.query(D)], [\n            {'k1': 'v1', 'k2': 'vxxx'},\n            {'k2': 'vxxx', 'k3': 'v3', 'k6': 'v6'}\n        ])\n\n        HStoreModel.update(data=D.delete('k1', 'k2')).execute()\n        self.assertEqual([x.data for x in self.query(D)], [\n            {},\n            {'k3': 'v3', 'k6': 'v6'}])\n\n\nclass TestArrayField(ModelTestCase):\n    database = db\n    requires = [ArrayModel]\n\n    def create_sample(self):\n        return ArrayModel.create(\n            tags=['alpha', 'beta', 'gamma', 'delta'],\n            ints=[[1, 2], [3, 4], [5, 6]])\n\n    def test_index_expression(self):\n        data = (\n            (['a', 'b', 'c'], []),\n            (['b', 'c', 'd', 'e'], []))\n        am_ids = []\n        for tags, ints in data:\n            am = ArrayModel.create(tags=tags, ints=ints)\n            am_ids.append(am.id)\n\n        last_tag = fn.array_upper(ArrayModel.tags, 1)\n        query = ArrayModel.select(ArrayModel.tags[last_tag]).tuples()\n        self.assertEqual(sorted([t for t, in query]), ['c', 'e'])\n\n        q = ArrayModel.select().where(ArrayModel.tags[last_tag] < 'd')\n        self.assertEqual([a.id for a in q], [am_ids[0]])\n\n        q = ArrayModel.select().where(ArrayModel.tags[last_tag] > 'd')\n        self.assertEqual([a.id for a in q], [am_ids[1]])\n\n    def test_hashable_objectslice(self):\n        ArrayModel.create(tags=[], ints=[[0, 1], [2, 3]])\n        ArrayModel.create(tags=[], ints=[[4, 5], [6, 7]])\n        n = (ArrayModel\n             .update({ArrayModel.ints[0][0]: ArrayModel.ints[0][0] + 1})\n             .execute())\n        self.assertEqual(n, 2)\n\n        am1, am2 = ArrayModel.select().order_by(ArrayModel.id)\n        self.assertEqual(am1.ints, [[1, 1], [2, 3]])\n        self.assertEqual(am2.ints, [[5, 5], [6, 7]])\n\n    def test_array_get_set(self):\n        am = self.create_sample()\n        am_db = ArrayModel.get(ArrayModel.id == am.id)\n        self.assertEqual(am_db.tags, ['alpha', 'beta', 'gamma', 'delta'])\n        self.assertEqual(am_db.ints, [[1, 2], [3, 4], [5, 6]])\n\n    def test_array_equality(self):\n        am1 = ArrayModel.create(tags=['t1'], ints=[[1, 2]])\n        am2 = ArrayModel.create(tags=['t2'], ints=[[3, 4]])\n\n        obj = ArrayModel.get(ArrayModel.tags == ['t1'])\n        self.assertEqual(obj.id, am1.id)\n        self.assertEqual(obj.tags, ['t1'])\n\n        obj = ArrayModel.get(ArrayModel.ints == [[3, 4]])\n        self.assertEqual(obj.id, am2.id)\n\n        obj = ArrayModel.get(ArrayModel.tags != ['t1'])\n        self.assertEqual(obj.id, am2.id)\n\n    def test_array_db_value(self):\n        am = ArrayModel.create(tags=('foo', 'bar'), ints=[])\n        am_db = ArrayModel.get(ArrayModel.id == am.id)\n        self.assertEqual(am_db.tags, ['foo', 'bar'])\n\n    def test_array_search(self):\n        def assertAM(where, *instances):\n            query = (ArrayModel\n                     .select()\n                     .where(where)\n                     .order_by(ArrayModel.id))\n            self.assertEqual([x.id for x in query], [x.id for x in instances])\n\n        am = self.create_sample()\n        am2 = ArrayModel.create(tags=['alpha', 'beta'], ints=[[1, 1]])\n        am3 = ArrayModel.create(tags=['delta'], ints=[[3, 4]])\n        am4 = ArrayModel.create(tags=['中文'], ints=[[3, 4]])\n        am5 = ArrayModel.create(tags=['中文', '汉语'], ints=[[3, 4]])\n\n        AM = ArrayModel\n        T = AM.tags\n\n        assertAM((Value('beta') == fn.ANY(T)), am, am2)\n        assertAM((Value('delta') == fn.Any(T)), am, am3)\n        assertAM(Value('omega') == fn.Any(T))\n\n        # Check the contains operator.\n        assertAM(SQL(\"tags::text[] @> ARRAY['beta']\"), am, am2)\n\n        # Use the nicer API.\n        assertAM(T.contains('beta'), am, am2)\n        assertAM(T.contains('omega', 'delta'))\n        assertAM(T.contains('汉语'), am5)\n        assertAM(T.contains('alpha', 'delta'), am)\n        assertAM(T.contained_by('alpha', 'beta', 'delta'), am2, am3)\n        assertAM(T.contained_by('alpha', 'beta', 'gamma', 'delta'),\n                 am, am2, am3)\n\n        # Check for any.\n        assertAM(T.contains_any('beta'), am, am2)\n        assertAM(T.contains_any('中文'), am4, am5)\n        assertAM(T.contains_any('omega', 'delta'), am, am3)\n        assertAM(T.contains_any('alpha', 'delta'), am, am2, am3)\n\n    def test_array_index_slice(self):\n        self.create_sample()\n        AM = ArrayModel\n        I, T = AM.ints, AM.tags\n\n        row = AM.select(T[1].alias('arrtags')).dicts().get()\n        self.assertEqual(row['arrtags'], 'beta')\n\n        row = AM.select(T[2:3].alias('foo')).dicts().get()\n        self.assertEqual(row['foo'], ['gamma'])\n\n        row = AM.select(T[:2].alias('foo')).dicts().get()\n        self.assertEqual(row['foo'], ['alpha', 'beta'])\n\n        row = AM.select(T[2:].alias('foo')).dicts().get()\n        self.assertEqual(row['foo'], ['gamma', 'delta'])\n\n        row = AM.select(T[2:4].alias('foo')).dicts().get()\n        self.assertEqual(row['foo'], ['gamma', 'delta'])\n\n        row = AM.select(I[1][1].alias('ints')).dicts().get()\n        self.assertEqual(row['ints'], 4)\n\n        row = AM.select(I[1:3][0].alias('ints')).dicts().get()\n        self.assertEqual(row['ints'], [[3], [5]])\n\n    @requires_models(DecimalArray)\n    def test_field_kwargs(self):\n        vl1, vl2 = [Dc('3.1'), Dc('1.3')], [Dc('3.14'), Dc('1')]\n        da1, da2 = [DecimalArray.create(values=vl) for vl in (vl1, vl2)]\n\n        da1_db = DecimalArray.get(DecimalArray.id == da1.id)\n        da2_db = DecimalArray.get(DecimalArray.id == da2.id)\n        self.assertEqual(da1_db.values, [Dc('3.1'), Dc('1.3')])\n        self.assertEqual(da2_db.values, [Dc('3.1'), Dc('1.0')])\n\n\nclass TestArrayFieldConvertValues(ModelTestCase):\n    database = db\n    requires = [ArrayTSModel]\n\n    def dt(self, day, hour=0, minute=0, second=0):\n        return datetime.datetime(2018, 1, day, hour, minute, second)\n\n    def test_value_conversion(self):\n\n        data = {\n            'k1': [self.dt(1), self.dt(2), self.dt(3)],\n            'k2': [],\n            'k3': [self.dt(4, 5, 6, 7), self.dt(10, 11, 12, 13)],\n        }\n        for key in sorted(data):\n            ArrayTSModel.create(key=key, timestamps=data[key])\n\n        for key in sorted(data):\n            am = ArrayTSModel.get(ArrayTSModel.key == key)\n            self.assertEqual(am.timestamps, data[key])\n\n        # Perform lookup using timestamp values.\n        ts = ArrayTSModel.get(ArrayTSModel.timestamps.contains(self.dt(3)))\n        self.assertEqual(ts.key, 'k1')\n\n        ts = ArrayTSModel.get(\n            ArrayTSModel.timestamps.contains(self.dt(4, 5, 6, 7)))\n        self.assertEqual(ts.key, 'k3')\n\n        self.assertRaises(ArrayTSModel.DoesNotExist, ArrayTSModel.get,\n                          ArrayTSModel.timestamps.contains(self.dt(4, 5, 6)))\n\n    def test_get_with_array_values(self):\n        a1 = ArrayTSModel.create(key='k1', timestamps=[self.dt(1)])\n        a2 = ArrayTSModel.create(key='k2', timestamps=[self.dt(2), self.dt(3)])\n\n        query = (ArrayTSModel\n                 .select()\n                 .where(ArrayTSModel.timestamps == [self.dt(1)]))\n        a1_db = query.get()\n        self.assertEqual(a1_db.id, a1.id)\n\n        query = (ArrayTSModel\n                 .select()\n                 .where(ArrayTSModel.timestamps == [self.dt(2), self.dt(3)]))\n        a2_db = query.get()\n        self.assertEqual(a2_db.id, a2.id)\n\n        a1_db = ArrayTSModel.get(timestamps=[self.dt(1)])\n        self.assertEqual(a1_db.id, a1.id)\n\n        a2_db = ArrayTSModel.get(timestamps=[self.dt(2), self.dt(3)])\n        self.assertEqual(a2_db.id, a2.id)\n\n\nclass TestArrayUUIDField(ModelTestCase):\n    database = db\n    requires = [UUIDList]\n\n    def test_array_of_uuids(self):\n        u1, u2, u3, u4 = [uuid.uuid4() for _ in range(4)]\n        a = UUIDList.create(key='a', id_list=[u1, u2, u3],\n                            id_list_native=[u1, u2, u3])\n        b = UUIDList.create(key='b', id_list=[u2, u3, u4],\n                            id_list_native=[u2, u3, u4])\n        a_db = UUIDList.get(UUIDList.key == 'a')\n        b_db = UUIDList.get(UUIDList.key == 'b')\n\n        self.assertEqual(a.id_list, [u1, u2, u3])\n        self.assertEqual(b.id_list, [u2, u3, u4])\n\n        self.assertEqual(a.id_list_native, [u1, u2, u3])\n        self.assertEqual(b.id_list_native, [u2, u3, u4])\n\n\nclass TestTSVectorField(ModelTestCase):\n    database = db\n    requires = [FTSModel]\n\n    messages = [\n        'A faith is a necessity to a man. Woe to him who believes in nothing.',\n        'All who call on God in true faith, earnestly from the heart, will '\n        'certainly be heard, and will receive what they have asked and desired.',\n        'Be faithful in small things because it is in them that your strength lies.',\n        'Faith consists in believing when it is beyond the power of reason to believe.',\n        'Faith has to do with things that are not seen and hope with things that are not at hand.',\n    ]\n\n    def setUp(self):\n        super(TestTSVectorField, self).setUp()\n        for idx, message in enumerate(self.messages):\n            FTSModel.create(title=str(idx), data=message,\n                            fts_data=fn.to_tsvector(message))\n\n    def assertMessages(self, expr, expected):\n        query = FTSModel.select().where(expr).order_by(FTSModel.id)\n        titles = [row.title for row in query]\n        self.assertEqual(list(map(int, titles)), expected)\n\n    def test_sql(self):\n        query = FTSModel.select().where(Match(FTSModel.data, 'foo bar'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"title\", \"t1\".\"data\", \"t1\".\"fts_data\" '\n            'FROM \"fts_model\" AS \"t1\" '\n            'WHERE (to_tsvector(\"t1\".\"data\") @@ to_tsquery(?))'), ['foo bar'])\n\n    def test_match_function(self):\n        D = FTSModel.data\n        self.assertMessages(Match(D, 'heart'), [1])\n        self.assertMessages(Match(D, 'god'), [1])\n        self.assertMessages(Match(D, 'faith'), [0, 1, 2, 3, 4])\n        self.assertMessages(Match(D, 'thing'), [2, 4])\n        self.assertMessages(Match(D, 'faith & things'), [2, 4])\n        self.assertMessages(Match(D, 'god | things'), [1, 2, 4])\n        self.assertMessages(Match(D, 'god & things'), [])\n\n    def test_tsvector_field(self):\n        M = FTSModel.fts_data.match\n        self.assertMessages(M('heart'), [1])\n        self.assertMessages(M('god'), [1])\n        self.assertMessages(M('faith'), [0, 1, 2, 3, 4])\n        self.assertMessages(M('thing'), [2, 4])\n        self.assertMessages(M('faith & things'), [2, 4])\n        self.assertMessages(M('god | things'), [1, 2, 4])\n        self.assertMessages(M('god & things'), [])\n\n        # Using the plain parser we cannot express \"OR\", but individual term\n        # match works like we expect and multi-term is AND-ed together.\n        self.assertMessages(M('god | things', plain=True), [])\n        self.assertMessages(M('god', plain=True), [1])\n        self.assertMessages(M('thing', plain=True), [2, 4])\n        self.assertMessages(M('faith things', plain=True), [2, 4])\n\n\ndef pg12():\n    with db:\n        return db.server_version >= 120000\n\n\nclass TestJsonField(BaseJsonFieldTestCase, ModelTestCase):\n    M = JsonModel\n    N = Normal\n    database = db\n    requires = [JsonModel, Normal, JsonModelNull]\n\n    def test_json_null(self):\n        tjn = JsonModelNull.create(data=None)\n        tj = JsonModelNull.create(data={'k1': 'v1'})\n\n        results = JsonModelNull.select().order_by(JsonModelNull.id)\n        self.assertEqual(\n            [tj_db.data for tj_db in results],\n            [None, {'k1': 'v1'}])\n\n        query = JsonModelNull.select().where(\n            JsonModelNull.data.is_null(True))\n        self.assertEqual(query.get(), tjn)\n\n\nclass TestBinaryJsonField(BaseBinaryJsonFieldTestCase, ModelTestCase):\n    M = BJson\n    N = Normal\n    database = db\n    requires = [BJson, Normal]\n\n    def test_remove_data(self):\n        BJson.delete().execute()  # Clear out db.\n        BJson.create(data={\n            'k1': 'v1',\n            'k2': 'v2',\n            'k3': {'x1': 'z1', 'x2': 'z2'},\n            'k4': [0, 1, 2]})\n\n        def assertData(exp_list, expected_data):\n            query = BJson.select(BJson.data.remove(*exp_list)).tuples()\n            data = query[:][0][0]\n            self.assertEqual(data, expected_data)\n\n        D = BJson.data\n        assertData(['k3'], {'k1': 'v1', 'k2': 'v2', 'k4': [0, 1, 2]})\n        assertData(['k1', 'k3'], {'k2': 'v2', 'k4': [0, 1, 2]})\n        assertData(['k1', 'kx', 'ky', 'k3'], {'k2': 'v2', 'k4': [0, 1, 2]})\n        assertData(['k4', 'k3'], {'k1': 'v1', 'k2': 'v2'})\n\n    def test_remove_path(self):\n        BJson.delete().execute()  # Clear out db.\n        data = {'k1': {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': ['i1', 'i2']}}\n        BJson.create(data=data)\n\n        def assertData(exp_list, expected_data):\n            curr = BJson.data\n            for exp in exp_list:\n                curr = curr[exp]\n\n            query = BJson.select(curr.remove()).tuples()\n            data = query[:][0][0]\n            self.assertEqual(data, expected_data)\n\n        assertData(['k1'], {})\n\n        assertData(['k1', 'x1'], {'k1': {'x2': ['i1', 'i2']}})\n\n        assertData(['k1', 'x1', 'y1'],\n                   {'k1': {'x1': {'y2': 'z2'}, 'x2': ['i1', 'i2']}})\n        assertData(['k1', 'x1', 'y2'],\n                   {'k1': {'x1': {'y1': 'z1'}, 'x2': ['i1', 'i2']}})\n\n        assertData(['k1', 'x2', 0],\n                   {'k1': {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': ['i2']}})\n        assertData(['k1', 'x2', -1],\n                   {'k1': {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': ['i1']}})\n\n        assertData(['kx'], data)\n        assertData(['k1', 'zz'], data)\n\n    def test_json_length(self):\n        BJson.delete().execute()  # Clear out db.\n        data = {'k1': {'x1': [1, 2, 3], 'x2': [1, 2], 'x3': []}}\n        BJson.create(data=data)\n\n        def assertLength(exp_list, count):\n            curr = BJson.data\n            for exp in exp_list:\n                curr = curr[exp]\n\n            query = BJson.select(curr.length()).tuples()\n            data = query[:][0][0]\n            self.assertEqual(data, count)\n\n        assertLength(('k1', 'x1'), 3)\n        assertLength(('k1', 'x2'), 2)\n        assertLength(('k1', 'x3'), 0)\n\n        BJson.delete().execute()  # Clear out db.\n        BJson.create(data=[0, 1, 2, 3, 4, 5])\n        assertLength((), 6)\n\n    def test_json_extract(self):\n        BJson.delete().execute()  # Clear out db.\n        data = {'k1': {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': ['i1', 'i2']}}\n        BJson.create(data=data)\n\n        def assertData(node, path, expected_data):\n            query = BJson.select(node.extract(*path)).tuples()\n            data = query[:][0][0]\n            self.assertEqual(data, expected_data)\n\n        assertData(BJson.data, ('k1', 'x1', 'y1'), 'z1')\n        assertData(BJson.data, ('k1', 'x1'), {'y1': 'z1', 'y2': 'z2'})\n        assertData(BJson.data, ('k1', 'x2', 0), 'i1')\n        assertData(BJson.data, ('k1', 'x2', -1), 'i2')\n        assertData(BJson.data, ('k1',),\n                   {'x1': {'y1': 'z1', 'y2': 'z2'}, 'x2': ['i1', 'i2']})\n        assertData(BJson.data, ('kx',), None)\n\n        assertData(BJson.data['k1'], ('x1', 'y1'), 'z1')\n        assertData(BJson.data['k1']['x1'], ('y1',), 'z1')\n        assertData(BJson.data['k1']['x2'], (0,), 'i1')\n\n    def test_json_contains_in_list(self):\n        m1 = self.M.create(data=[{'k1': 'v1', 'k2': 'v2'}, {'a1': 'b1'}])\n        m2 = self.M.create(data=[{'k3': 'v3'}, {'k4': 'v4'}])\n        m3 = self.M.create(data=[{'k5': 'v5', 'k6': 'v6'}, {'k1': 'v1'}])\n\n        query = (self.M\n                 .select()\n                 .where(self.M.data.contains([{'k1': 'v1'}]))\n                 .order_by(self.M.id))\n        self.assertEqual([m.id for m in query], [m1.id, m3.id])\n\n    def test_integer_index_weirdness(self):\n        self._create_test_data()\n\n        def fails():\n            with self.database.atomic():\n                expr = BJson.data.contains_any(2, 8, 12)\n                results = list(BJson.select().where(\n                    BJson.data.contains_any(2, 8, 12)))\n\n        # Complains of a missing cast/conversion for the data-type?\n        self.assertRaises(ProgrammingError, fails)\n\n\nclass TestJsonFieldRegressions(ModelTestCase):\n    database = db\n    requires = [JData]\n\n    def test_json_field_concat(self):\n        jd = JData.create(\n            d1={'k1': {'x1': 'y1'}, 'k2': 'v2', 'k3': 'v3'},\n            d2={'k1': {'x2': 'y2'}, 'k2': 'v2-x', 'k4': 'v4'})\n\n        query = JData.select(JData.d1.concat(JData.d2).alias('data'))\n        obj = query.get()\n        self.assertEqual(obj.data, {\n            'k1': {'x2': 'y2'}, 'k2': 'v2-x', 'k3': 'v3', 'k4': 'v4'})\n\n    def test_introspect_bjson_field(self):\n        introspector = Introspector.from_database(self.database)\n        models = introspector.generate_models(table_names=['j_data'])\n        JD = models['j_data']\n        self.assertEqual(JD._meta.sorted_field_names, ['id', 'd1', 'd2'])\n        self.assertTrue(isinstance(JD.d1, BinaryJSONField))\n        self.assertTrue(isinstance(JD.d2, BinaryJSONField))\n        self.assertTrue(JD.d1.index)\n        self.assertEqual(JD.d1.index_type, 'GIN')\n        self.assertFalse(JD.d2.index)\n\n\nclass TestJSONFieldCustomDumps(ModelTestCase):\n    database = db\n    requires = [CustomJSONDumps]\n\n    def test_custom_dumps(self):\n        u1 = uuid.uuid4()\n        u2 = uuid.uuid4()\n\n        data = {'u1': u1, 'u2': u2, 'u3': [u1, u2]}\n        c = CustomJSONDumps.create(jf=data, jbf=data)\n        c_db = CustomJSONDumps.get_by_id(c.id)\n\n        self.assertEqual(c_db.jf, {\n            'u1': str(u1),\n            'u2': str(u2),\n            'u3': [str(u1), str(u2)]})\n        self.assertEqual(c_db.jbf, {\n            'u1': str(u1),\n            'u2': str(u2),\n            'u3': [str(u1), str(u2)]})\n\n\nclass TestIntervalField(ModelTestCase):\n    database = db\n    requires = [Event]\n\n    def test_interval_field(self):\n        e1 = Event.create(name='hour', duration=datetime.timedelta(hours=1))\n        e2 = Event.create(name='mix', duration=datetime.timedelta(\n            days=1,\n            hours=2,\n            minutes=3,\n            seconds=4))\n\n        events = [(e.name, e.duration)\n                  for e in Event.select().order_by(Event.duration)]\n        self.assertEqual(events, [\n            ('hour', datetime.timedelta(hours=1)),\n            ('mix', datetime.timedelta(days=1, hours=2, minutes=3, seconds=4))\n        ])\n\n\nclass TestIndexedField(BaseTestCase):\n    def test_indexed_field_ddl(self):\n        class FakeIndexedField(IndexedFieldMixin, CharField):\n            default_index_type = 'GiST'\n\n        class IndexedModel(TestModel):\n            array_index = ArrayField(CharField)\n            array_noindex= ArrayField(IntegerField, index=False)\n            fake_index = FakeIndexedField()\n            fake_index_with_type = FakeIndexedField(index_type='MAGIC')\n            fake_noindex = FakeIndexedField(index=False)\n\n            class Meta:\n                database = db\n\n        create_sql, _ = IndexedModel._schema._create_table(False).query()\n        self.assertEqual(create_sql, (\n            'CREATE TABLE \"indexed_model\" ('\n            '\"id\" SERIAL NOT NULL PRIMARY KEY, '\n            '\"array_index\" VARCHAR(255)[] NOT NULL, '\n            '\"array_noindex\" INTEGER[] NOT NULL, '\n            '\"fake_index\" VARCHAR(255) NOT NULL, '\n            '\"fake_index_with_type\" VARCHAR(255) NOT NULL, '\n            '\"fake_noindex\" VARCHAR(255) NOT NULL)'))\n\n        indexes = [idx.query()[0]\n                   for idx in IndexedModel._schema._create_indexes(False)]\n        self.assertEqual(indexes, [\n            ('CREATE INDEX \"indexed_model_array_index\" ON \"indexed_model\" '\n             'USING GIN (\"array_index\")'),\n            ('CREATE INDEX \"indexed_model_fake_index\" ON \"indexed_model\" '\n             'USING GiST (\"fake_index\")'),\n            ('CREATE INDEX \"indexed_model_fake_index_with_type\" '\n             'ON \"indexed_model\" '\n             'USING MAGIC (\"fake_index_with_type\")')])\n\n\nclass IDAlways(TestModel):\n    id = IdentityField(generate_always=True)\n    data = CharField()\n\n\nclass IDByDefault(TestModel):\n    id = IdentityField()\n    data = CharField()\n\n\nclass TestIdentityField(ModelTestCase):\n    database = db\n    requires = [IDAlways, IDByDefault]\n\n    def test_identity_field_always(self):\n        iq = IDAlways.insert_many([(d,) for d in ('d1', 'd2', 'd3')])\n        curs = iq.execute()\n        self.assertEqual(list(curs), [(1,), (2,), (3,)])\n\n        # Cannot specify id when generate always is true.\n        with self.assertRaises(ProgrammingError):\n            with self.database.atomic():\n                IDAlways.create(id=10, data='d10')\n\n        query = IDAlways.select().order_by(IDAlways.id)\n        self.assertEqual(list(query.tuples()), [\n            (1, 'd1'), (2, 'd2'), (3, 'd3')])\n\n    def test_identity_field_by_default(self):\n        iq = IDByDefault.insert_many([(d,) for d in ('d1', 'd2', 'd3')])\n        curs = iq.execute()\n        self.assertEqual(list(curs), [(1,), (2,), (3,)])\n\n        # Cannot specify id when generate always is true.\n        IDByDefault.create(id=10, data='d10')\n\n        query = IDByDefault.select().order_by(IDByDefault.id)\n        self.assertEqual(list(query.tuples()), [\n            (1, 'd1'), (2, 'd2'), (3, 'd3'), (10, 'd10')])\n\n    def test_schema(self):\n        sql, params = IDAlways._schema._create_table(False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"id_always\" (\"id\" INT GENERATED ALWAYS AS IDENTITY '\n            'NOT NULL PRIMARY KEY, \"data\" VARCHAR(255) NOT NULL)'))\n\n        sql, params = IDByDefault._schema._create_table(False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"id_by_default\" (\"id\" INT GENERATED BY DEFAULT AS '\n            'IDENTITY NOT NULL PRIMARY KEY, \"data\" VARCHAR(255) NOT NULL)'))\n\n\nclass TestServerSide(ModelTestCase):\n    database = db\n    requires = [Register]\n\n    def setUp(self):\n        super(TestServerSide, self).setUp()\n        with db.atomic():\n            for i in range(100):\n                Register.create(value=i)\n\n    def test_server_side_cursor(self):\n        query = Register.select().order_by(Register.value)\n        with self.database.atomic():\n            with self.assertQueryCount(1):\n                data = [row.value for row in ServerSide(query)]\n                self.assertEqual(data, list(range(100)))\n\n            ss_query = ServerSide(query.limit(10), array_size=3)\n            self.assertEqual([row.value for row in ss_query], list(range(10)))\n\n            ss_query = ServerSide(query.where(SQL('1 = 0')))\n            self.assertEqual(list(ss_query), [])\n\n    def test_lower_level_apis(self):\n        query = Register.select(Register.value).order_by(Register.value)\n        with self.database.atomic():\n            ssq = ServerSideQuery(query, array_size=10)\n            curs_wrapper = ssq._execute(self.database)\n            curs = curs_wrapper.cursor\n            self.assertTrue(isinstance(curs, FetchManyCursor))\n            self.assertEqual(curs.fetchone(), (0,))\n            self.assertEqual(curs.fetchone(), (1,))\n            curs.close()\n\n    def test_close_cursor(self):\n        query = Register.select(Register.value).order_by(Register.value)\n        with self.database.atomic():\n            ssq = ServerSideQuery(query, array_size=10)\n            accum = []\n            for i, obj in enumerate(ssq.iterator()):\n                if i == 25:\n                    break\n                accum.append(obj.value)\n\n            self.assertTrue(ssq.close())\n\n        self.assertEqual(len(accum), 25)\n        self.assertEqual(accum, list(range(25)))\n\n\nclass KX(TestModel):\n    key = CharField(unique=True)\n    value = IntegerField()\n\nclass TestAutocommitIntegration(ModelTestCase):\n    database = db\n    requires = [KX]\n\n    def setUp(self):\n        super(TestAutocommitIntegration, self).setUp()\n        with self.database.atomic():\n            kx1 = KX.create(key='k1', value=1)\n\n    def force_integrity_error(self):\n        # Force an integrity error, then verify that the current\n        # transaction has been aborted.\n        self.assertRaises(IntegrityError, KX.create, key='k1', value=10)\n\n    def test_autocommit_default(self):\n        kx2 = KX.create(key='k2', value=2)  # Will be committed.\n        self.assertTrue(kx2.id > 0)\n        self.force_integrity_error()\n\n        self.assertEqual(KX.select().count(), 2)\n        self.assertEqual([(kx.key, kx.value)\n                          for kx in KX.select().order_by(KX.key)],\n                         [('k1', 1), ('k2', 2)])\n\n    def test_autocommit_disabled(self):\n        with self.database.manual_commit():\n            self.database.begin()\n            kx2 = KX.create(key='k2', value=2)  # Not committed.\n            self.assertTrue(kx2.id > 0)  # Yes, we have a primary key.\n            self.force_integrity_error()\n            self.database.rollback()\n\n        self.assertEqual(KX.select().count(), 1)\n        kx1_db = KX.get(KX.key == 'k1')\n        self.assertEqual(kx1_db.value, 1)\n\n    def test_atomic_block(self):\n        with self.database.atomic() as txn:\n            kx2 = KX.create(key='k2', value=2)\n            self.assertTrue(kx2.id > 0)\n            self.force_integrity_error()\n            txn.rollback(False)\n\n        self.assertEqual(KX.select().count(), 1)\n        kx1_db = KX.get(KX.key == 'k1')\n        self.assertEqual(kx1_db.value, 1)\n\n    def test_atomic_block_exception(self):\n        with self.assertRaises(IntegrityError):\n            with self.database.atomic():\n                KX.create(key='k2', value=2)\n                KX.create(key='k1', value=10)\n\n        self.assertEqual(KX.select().count(), 1)\n\n\nclass TestPostgresIsolationLevel(DatabaseTestCase):\n    database = db_loader('postgres', isolation_level=3)  # SERIALIZABLE.\n\n    def test_isolation_level(self):\n        conn = self.database.connection()\n        self.assertEqual(conn.isolation_level, 3)\n\n        conn.set_isolation_level(2)\n        self.assertEqual(conn.isolation_level, 2)\n        self.database.close()\n\n        conn = self.database.connection()\n        self.assertEqual(conn.isolation_level, 3)\n        self.database.close()\n\n        self.database.set_isolation_level(2)\n        for _ in range(2):\n            conn = self.database.connection()\n            self.assertEqual(conn.isolation_level, 2)\n            self.database.close()\n\n    def test_isolation_level_str(self):\n        db = db_loader('postgres', isolation_level='SERIALIZABLE')\n        conn = db.connection()\n        self.assertEqual(conn.isolation_level,\n                         db._adapter.isolation_levels_inv['SERIALIZABLE'])\n        db.close()\n\n        db.set_isolation_level('READ COMMITTED')\n        conn = db.connection()\n        self.assertEqual(conn.isolation_level,\n                         db._adapter.isolation_levels_inv['READ COMMITTED'])\n        db.close()\n\n    def test_isolation_level_mixed(self):\n        db = db_loader('postgres', isolation_level='SERIALIZABLE')\n        conn = db.connection()\n        self.assertEqual(conn.isolation_level,\n                         db._adapter.isolation_levels_inv['SERIALIZABLE'])\n        db.close()\n\n        rc = db._adapter.isolation_levels_inv['READ COMMITTED']\n        db.set_isolation_level(rc)\n        conn = db.connection()\n        self.assertEqual(conn.isolation_level, rc)\n        db.close()\n\n\n@skip_unless(pg12(), 'cte materialization requires pg >= 12')\nclass TestPostgresCTEMaterialization(ModelTestCase):\n    database = db\n    requires = [Register]\n\n    def test_postgres_cte_materialization(self):\n        Register.insert_many([(i,) for i in (1, 2, 3)]).execute()\n\n        for materialized in (None, False, True):\n            cte = Register.select().cte('t', materialized=materialized)\n            query = (cte\n                     .select_from(cte.c.value)\n                     .where(cte.c.value != 2)\n                     .order_by(cte.c.value))\n            self.assertEqual([r.value for r in query], [1, 3])\n\n\nclass TestPostgresLateralJoin(ModelTestCase):\n    database = db\n    test_data = (\n        ('a', (('a1', 1),\n               ('a2', 2),\n               ('a10', 10))),\n        ('b', (('b3', 3),\n               ('b4', 4),\n               ('b7', 7))),\n        ('c', ()))\n\n    def create_data(self):\n        ts = lambda d: datetime.datetime(2019, 1, d)\n        with self.database.atomic():\n            for username, tweets in self.test_data:\n                user = User.create(username=username)\n                for c, d in tweets:\n                    Tweet.create(user=user, content=c, timestamp=ts(d))\n\n    @requires_models(User, Tweet)\n    def test_lateral_top_n(self):\n        self.create_data()\n\n        subq = (Tweet\n                .select(Tweet.content, Tweet.timestamp)\n                .where(Tweet.user == User.id)\n                .order_by(Tweet.timestamp.desc())\n                .limit(2))\n        query = (User\n                 .select(User, subq.c.content)\n                 .join(subq, JOIN.LEFT_LATERAL)\n                 .order_by(subq.c.timestamp.desc(nulls='last')))\n        results = [(u.username, u.content) for u in query]\n        self.assertEqual(results, [\n            ('a', 'a10'),\n            ('b', 'b7'),\n            ('b', 'b4'),\n            ('a', 'a2'),\n            ('c', None)])\n\n        query = (Tweet\n                 .select(User.username, subq.c.content)\n                 .from_(User)\n                 .join(subq, JOIN.LEFT_LATERAL)\n                 .order_by(User.username, subq.c.timestamp))\n\n        results = [(t.username, t.content) for t in query]\n        self.assertEqual(results, [\n            ('a', 'a2'),\n            ('a', 'a10'),\n            ('b', 'b4'),\n            ('b', 'b7'),\n            ('c', None)])\n\n    @requires_models(User, Tweet)\n    def test_lateral_helper(self):\n        self.create_data()\n\n        subq = (Tweet\n                .select(Tweet.content, Tweet.timestamp)\n                .where(Tweet.user == User.id)\n                .order_by(Tweet.timestamp.desc())\n                .limit(2)\n                .lateral())\n\n        query = (User\n                 .select(User, subq.c.content)\n                 .join(subq, on=True)\n                 .order_by(subq.c.timestamp.desc(nulls='last')))\n        with self.assertQueryCount(1):\n            results = [(u.username, u.tweet.content) for u in query]\n            self.assertEqual(results, [\n                ('a', 'a10'),\n                ('b', 'b7'),\n                ('b', 'b4'),\n                ('a', 'a2')])\n"
  },
  {
    "path": "tests/postgres_helpers.py",
    "content": "from peewee import Cast\n\n\nclass BaseJsonFieldTestCase(object):\n    # Subclasses must define these, as well as specifying requires[].\n    M = None  # Json model.\n    N = None  # \"Normal\" model.\n\n    def test_json_field(self):\n        data = {'k1': ['a1', 'a2'], 'k2': {'k3': 'v3'}}\n        j = self.M.create(data=data)\n        j_db = self.M.get(j._pk_expr())\n        self.assertEqual(j_db.data, data)\n\n    def test_joining_on_json_key(self):\n        values = [\n            {'foo': 'bar', 'baze': {'nugget': 'alpha'}},\n            {'foo': 'bar', 'baze': {'nugget': 'beta'}},\n            {'herp': 'derp', 'baze': {'nugget': 'epsilon'}},\n            {'herp': 'derp', 'bar': {'nuggie': 'alpha'}},\n        ]\n        for data in values:\n            self.M.create(data=data)\n\n        for value in ['alpha', 'beta', 'gamma', 'delta']:\n            self.N.create(data=value)\n\n        query = (self.M\n                 .select()\n                 .join(self.N, on=(\n                     self.N.data == self.M.data['baze']['nugget']))\n                 .order_by(self.M.id))\n        results = [jm.data for jm in query]\n        self.assertEqual(results, [\n            {'foo': 'bar', 'baze': {'nugget': 'alpha'}},\n            {'foo': 'bar', 'baze': {'nugget': 'beta'}},\n        ])\n\n    def test_json_lookup_methods(self):\n        data = {\n            'gp1': {\n                'p1': {'c1': 'foo'},\n                'p2': {'c2': 'bar'}},\n            'gp2': {}}\n        j = self.M.create(data=data)\n\n        def assertLookup(lookup, expected):\n            query = (self.M\n                     .select(lookup)\n                     .where(j._pk_expr())\n                     .dicts())\n            self.assertEqual(query.get(), expected)\n\n        expr = self.M.data['gp1']['p1']\n        assertLookup(expr.alias('p1'), {'p1': '{\"c1\": \"foo\"}'})\n        assertLookup(expr.as_json().alias('p2'), {'p2': {'c1': 'foo'}})\n\n        expr = self.M.data['gp1']['p1']['c1']\n        assertLookup(expr.alias('c1'), {'c1': 'foo'})\n        assertLookup(expr.as_json().alias('c2'), {'c2': 'foo'})\n\n        j.data = [\n            {'i1': ['foo', 'bar', 'baz']},\n            ['nugget', 'mickey']]\n        j.save()\n\n        expr = self.M.data[0]['i1']\n        assertLookup(expr.alias('i1'), {'i1': '[\"foo\", \"bar\", \"baz\"]'})\n        assertLookup(expr.as_json().alias('i2'), {'i2': ['foo', 'bar', 'baz']})\n\n        expr = self.M.data[1][1]\n        assertLookup(expr.alias('l1'), {'l1': 'mickey'})\n        assertLookup(expr.as_json().alias('l2'), {'l2': 'mickey'})\n\n    def test_json_cast(self):\n        self.M.create(data={'foo': {'bar': 3}})\n        self.M.create(data={'foo': {'bar': 5}})\n        query = (self.M\n                 .select(Cast(self.M.data['foo']['bar'], 'float') * 1.5)\n                 .order_by(self.M.id)\n                 .tuples())\n        self.assertEqual(query[:], [(4.5,), (7.5,)])\n\n    def test_json_path(self):\n        data = {\n            'foo': {\n                'baz': {\n                    'bar': ['i1', 'i2', 'i3'],\n                    'baze': ['j1', 'j2'],\n                }}}\n        j = self.M.create(data=data)\n\n        def assertPath(path, expected):\n            query = (self.M\n                     .select(path)\n                     .where(j._pk_expr())\n                     .dicts())\n            self.assertEqual(query.get(), expected)\n\n        expr = self.M.data.path('foo', 'baz', 'bar')\n        assertPath(expr.alias('p1'), {'p1': ['i1', 'i2', 'i3']})\n        assertPath(expr.alias('p2'), {'p2': ['i1', 'i2', 'i3']})\n\n        expr = self.M.data.path('foo', 'baz', 'baze', 1)\n        assertPath(expr.alias('p1'), {'p1': 'j2'})\n        assertPath(expr.alias('p2'), {'p2': 'j2'})\n\n        expr = self.M.data['foo'].path('baz', 'bar')\n        assertPath(expr.as_json(False).alias('p1'), {'p1': '[\"i1\", \"i2\", \"i3\"]'})\n        assertPath(expr.alias('p2'), {'p2': ['i1', 'i2', 'i3']})\n\n    def test_json_field_sql(self):\n        j = (self.M\n             .select()\n             .where(self.M.data == {'foo': 'bar'}))\n        table = self.M._meta.table_name\n        self.assertSQL(j, (\n            'SELECT \"t1\".\"id\", \"t1\".\"data\" '\n            'FROM \"%s\" AS \"t1\" WHERE (\"t1\".\"data\" = ?)')\n            % table)\n\n        j = (self.M\n             .select()\n             .where(self.M.data['foo'] == 'bar'))\n        self.assertSQL(j, (\n            'SELECT \"t1\".\"id\", \"t1\".\"data\" '\n            'FROM \"%s\" AS \"t1\" WHERE (\"t1\".\"data\"->>? = ?)') % table)\n\n    def assertItems(self, where, *items):\n        query = (self.M\n                 .select()\n                 .where(where)\n                 .order_by(self.M.id))\n        self.assertEqual(\n            [item.id for item in query],\n            [item.id for item in items])\n\n    def test_lookup(self):\n        t1 = self.M.create(data={'k1': 'v1', 'k2': {'k3': 'v3'}})\n        t2 = self.M.create(data={'k1': 'x1', 'k2': {'k3': 'x3'}})\n        t3 = self.M.create(data={'k1': 'v1', 'j2': {'j3': 'v3'}})\n        self.assertItems((self.M.data['k2']['k3'] == 'v3'), t1)\n        self.assertItems((self.M.data['k1'] == 'v1'), t1, t3)\n\n        # Valid key, no matching value.\n        self.assertItems((self.M.data['k2'] == 'v1'))\n\n        # Non-existent key.\n        self.assertItems((self.M.data['not-here'] == 'v1'))\n\n        # Non-existent nested key.\n        self.assertItems((self.M.data['not-here']['xxx'] == 'v1'))\n\n        self.assertItems((self.M.data['k2']['xxx'] == 'v1'))\n\n    def test_bulk_update(self):\n        m1 = self.M.create(data={'k1': 'v1'})\n        m2 = self.M.create(data={'k2': 'v2'})\n        m3 = self.M.create(data=['i1', 'i2'])\n        m1.data['k1'] = 'v1-x'\n        m2.data['k2'] = 'v2-y'\n        m3.data.append('i3')\n        self.M.bulk_update([m1, m2, m3], fields=[self.M.data])\n\n        m1_db = self.M.get(self.M.id == m1.id)\n        m2_db = self.M.get(self.M.id == m2.id)\n        m3_db = self.M.get(self.M.id == m3.id)\n        self.assertEqual(m1_db.data, {'k1': 'v1-x'})\n        self.assertEqual(m2_db.data, {'k2': 'v2-y'})\n        self.assertEqual(m3_db.data, ['i1', 'i2', 'i3'])\n\n    def test_json_bulk_update_top_level_list(self):\n        m1 = self.M.create(data=['a', 'b', 'c'])\n        m2 = self.M.create(data=['d', 'e', 'f'])\n\n        m1.data = ['g', 'h', 'i', {'j': 'kk'}]\n        m2.data = ['j', 'k', 'l']\n        self.M.bulk_update([m1, m2], fields=[self.M.data])\n        m1_db = self.M.get(self.M.id == m1.id)\n        m2_db = self.M.get(self.M.id == m2.id)\n        self.assertEqual(m1_db.data, ['g', 'h', 'i', {'j': 'kk'}])\n        self.assertEqual(m2_db.data, ['j', 'k', 'l'])\n\n\n# Contains additional test-cases suitable for the JSONB data-type.\nclass BaseBinaryJsonFieldTestCase(BaseJsonFieldTestCase):\n    def _create_test_data(self):\n        data = [\n            {'k1': 'v1', 'k2': 'v2', 'k3': {'k4': ['i1', 'i2'], 'k5': {}}},\n            ['a1', 'a2', {'a3': 'a4'}],\n            {'a1': 'x1', 'a2': 'x2', 'k4': ['i1', 'i2']},\n            list(range(10)),\n            list(range(5, 15)),\n            ['k4', 'k1']]\n\n        self._bjson_objects = []\n        for json_value in data:\n            self._bjson_objects.append(self.M.create(data=json_value))\n\n    def assertObjects(self, expr, *indexes):\n        query = (self.M\n                 .select()\n                 .where(expr)\n                 .order_by(self.M.id))\n        self.assertEqual(\n            [bjson.data for bjson in query],\n            [self._bjson_objects[index].data for index in indexes])\n\n    def test_contained_by(self):\n        self._create_test_data()\n        D = self.M.data\n\n        item1 = ['a1', 'a2', {'a3': 'a4'}, 'a5']\n        self.assertObjects(D.contained_by(item1), 1)\n\n        item2 = {'a1': 'x1', 'a2': 'x2', 'k4': ['i0', 'i1', 'i2'], 'x': 'y'}\n        self.assertObjects(D.contained_by(item2), 2)\n\n        self.assertObjects(D.contained_by(list(range(10))), 3)\n        self.assertObjects(D.contained_by(list(range(20))), 3, 4)\n\n    def test_contained_by_nested(self):\n        self._create_test_data()\n        D = self.M.data\n        self.assertObjects(D['k4'].contained_by(['i1', 'i2']), 2)\n        self.assertObjects(D['k3']['k4'].contained_by(['i1', 'i2']), 0)\n        self.assertObjects(D[2].contained_by({'a3': 'a4'}), 1)\n        self.assertObjects(D[0].contained_by(['a1', 'ax']), 1)\n\n    def test_equality(self):\n        data = {'k1': ['a1', 'a2'], 'k2': {'k3': 'v3'}}\n        j = self.M.create(data=data)\n        j_db = self.M.get(self.M.data == data)\n        self.assertEqual(j.id, j_db.id)\n\n    def test_subscript_contains(self):\n        self._create_test_data()\n        D = self.M.data\n\n        # 'k3' is mapped to another dictioary {'k4': [...]}. Therefore,\n        # 'k3' is said to contain 'k4', but *not* ['k4'] or ['k4', 'k5'].\n        self.assertObjects(D['k3'].has_key('k4'), 0)\n        self.assertObjects(D['k3'].contains(['k4']))\n        self.assertObjects(D['k3'].contains(['k4', 'k5']))\n\n        # We can check for the keys this way, though.\n        self.assertObjects(D['k3'].contains_all('k4', 'k5'), 0)\n        self.assertObjects(D['k3'].contains_any('k4', 'kx'), 0)\n\n        # However, in test object index=2, 'k4' can be said to contain\n        # both 'i1' and ['i1'].\n        self.assertObjects(D['k4'].contains('i1'), 2)\n        self.assertObjects(D['k4'].contains(['i1']), 2)\n\n        # Interestingly, we can also specify the list of contained values\n        # out-of-order.\n        self.assertObjects(D['k4'].contains(['i2', 'i1']), 2)\n\n        # We can test whether an object contains another JSON object fragment.\n        self.assertObjects(D['k3'].contains({'k4': ['i1']}), 0)\n        self.assertObjects(D['k3'].contains({'k4': ['i1', 'i2']}), 0)\n\n        # Check multiple levels of nesting / containment.\n        self.assertObjects(D['k3']['k4'].contains('i2'), 0)\n        self.assertObjects(D['k3']['k4'].contains_all('i1', 'i2'), 0)\n        self.assertObjects(D['k3']['k4'].contains_all('i0', 'i2'))\n        self.assertObjects(D['k4'].contains_all('i1', 'i2'), 2)\n\n        # Check array indexes.\n        self.assertObjects(D[2].has_key('a3'), 1)\n        self.assertObjects(D[2].contains('a3'))\n        self.assertObjects(D[0].contains('a1'), 1)\n        self.assertObjects(D[0].contains('k1'))\n\n    def test_contains(self):\n        self._create_test_data()\n        D = self.M.data\n\n        # Test for keys. 'k4' is both an object key and an array element.\n        self.assertObjects(D.has_key('k4'), 2, 5)\n        self.assertObjects(D.has_key('a1'), 1, 2)\n        self.assertObjects(D.contains('a1'), 1)\n        self.assertObjects(D.has_key('k3'), 0)\n\n        # We can test for multiple top-level keys/indexes.\n        self.assertObjects(D.contains_all('a1', 'a2'), 1, 2)\n\n        # If we test for both with .contains(), though, it is treated as\n        # an object match.\n        self.assertObjects(D.contains(['a1', 'a2']), 1)\n\n        # Check numbers.\n        self.assertObjects(D.contains([2, 5, 6, 7, 8]), 3)\n        self.assertObjects(D.contains([5, 6, 7, 8, 9]), 3, 4)\n\n        # We can check for partial objects.\n        self.assertObjects(D.contains({'a1': 'x1'}), 2)\n        self.assertObjects(D.contains({'k3': {'k4': []}}), 0)\n        self.assertObjects(D.contains([{'a3': 'a4'}]), 1)\n\n        # Check for simple keys.\n        self.assertObjects(D.contains(['a1']), 1)\n        self.assertObjects(D.contains('a1'), 1)\n        self.assertObjects(D.contains('k3'))\n\n        # Contains any.\n        self.assertObjects(D.contains_any('a1', 'k1'), 0, 1, 2, 5)\n        self.assertObjects(D.contains_any('k4', 'xx', 'yy', '2'), 2, 5)\n        self.assertObjects(D.contains_any('i1', 'i2', 'a3'))\n\n        # Contains all.\n        self.assertObjects(D.contains_all('k1', 'k2', 'k3'), 0)\n        self.assertObjects(D.contains_all('k1', 'k2', 'k3', 'k4'))\n\n        # Has key.\n        self.assertObjects(D.has_key('a1'), 1, 2)\n        self.assertObjects(D.has_key('k1'), 0, 5)\n        self.assertObjects(D.has_key('k4'), 2, 5)\n        self.assertObjects(D.has_key('a3'))\n\n        self.assertObjects(D['k3'].has_key('k4'), 0)\n        self.assertObjects(D['k4'].has_key('i2'), 2)\n\n    def test_contains_contained_by(self):\n        samples = (\n            {'k1': 'v1', 'k2': 'v2'},\n            {'k1': 'v10'},\n            ['i1', 'i2', 'i3', 'test'],\n            'k1',\n            123,\n            1.5,\n            True,\n            False)\n        pks = []\n        for sample in samples:\n            pks.append(self.M.create(data=sample).id)\n\n        for i, sample in enumerate(samples):\n            q = self.M.select().where(self.M.data.contains(sample))\n            self.assertEqual([x.id for x in q], [pks[i]])\n\n            q = self.M.select().where(self.M.data.contained_by(sample))\n            self.assertEqual([x.id for x in q], [pks[i]])\n\n    def test_concat_data(self):\n        self.M.delete().execute()\n        self.M.create(data={'k1': {'x1': 'y1'}, 'k2': 'v2', 'k3': [0, 1]})\n\n        def assertData(exp, expected_data):\n            query = self.M.select(self.M.data.concat(exp)).tuples()\n            data = query[:][0][0]\n            self.assertEqual(data, expected_data)\n\n        D = self.M.data\n        assertData({'k2': 'v2-x', 'k1': {'x2': 'y2'}, 'k4': 'v4'}, {\n            'k1': {'x2': 'y2'},  # NB: not merged/patched!!\n            'k2': 'v2-x',\n            'k3': [0, 1],\n            'k4': 'v4'})\n        assertData({'k1': 'v1-x', 'k3': [2, 3, 4], 'k4': {'x4': 'y4'}}, {\n            'k1': 'v1-x',\n            'k2': 'v2',\n            'k3': [2, 3, 4],\n            'k4': {'x4': 'y4'}})\n\n        # We can update sub-keys.\n        query = self.M.select(D['k1'].concat({'x2': 'y2', 'x3': 'y3'}))\n        self.assertEqual(query.tuples()[0][0],\n                         {'x1': 'y1', 'x2': 'y2', 'x3': 'y3'})\n\n        # Concat can be used to extend JSON arrays.\n        query = self.M.select(D['k3'].concat([2, 3]))\n        self.assertEqual(query.tuples()[0][0], [0, 1, 2, 3])\n\n    def test_update_data_inplace(self):\n        self.M.delete().execute()\n        b = self.M.create(data={'k1': {'x1': 'y1'}, 'k2': 'v2'})\n\n        self.M.update(data=self.M.data.concat({\n            'k1': {'x2': 'y2'},\n            'k3': 'v3'})).execute()\n        b2 = self.M.get(self.M.id == b.id)\n        self.assertEqual(b2.data, {'k1': {'x2': 'y2'}, 'k2': 'v2', 'k3': 'v3'})\n\n    def test_selecting(self):\n        self._create_test_data()\n        query = (self.M\n                 .select(self.M.data['k3']['k4'].as_json().alias('k3k4'))\n                 .order_by(self.M.id))\n        k3k4_data = [obj.k3k4 for obj in query]\n        self.assertEqual(k3k4_data, [\n            ['i1', 'i2'],\n            None,\n            None,\n            None,\n            None,\n            None])\n\n        query = (self.M\n                 .select(\n                     self.M.data[0].as_json(),\n                     self.M.data[2].as_json())\n                 .order_by(self.M.id)\n                 .tuples())\n        self.assertEqual(list(query), [\n            (None, None),\n            ('a1', {'a3': 'a4'}),\n            (None, None),\n            (0, 2),\n            (5, 7),\n            ('k4', None)])\n\n    def test_conflict_update(self):\n        b1 = self.M.create(data={'k1': 'v1'})\n        iq = (self.M\n              .insert(id=b1.id, data={'k1': 'v1-x'})\n              .on_conflict('update', conflict_target=[self.M.id],\n                           update={self.M.data: {'k1': 'v1-z'}}))\n        b1_id_db = iq.execute()\n        self.assertEqual(b1.id, b1_id_db)\n\n        b1_db = self.M.get(self.M.id == b1.id)\n        self.assertEqual(self.M.data, {'k1': 'v1-z'})\n\n        iq = (self.M\n              .insert(id=b1.id, data={'k1': 'v1-y'})\n              .on_conflict('update', conflict_target=[self.M.id],\n                           update={'data': {'k1': 'v1-w'}}))\n        b1_id_db = iq.execute()\n        self.assertEqual(b1.id, b1_id_db)\n\n        b1_db = self.M.get(self.M.id == b1.id)\n        self.assertEqual(self.M.data, {'k1': 'v1-w'})\n\n        self.assertEqual(self.M.select().count(), 1)\n"
  },
  {
    "path": "tests/prefetch_tests.py",
    "content": "from peewee import *\n\nfrom .base import get_in_memory_db\nfrom .base import requires_models\nfrom .base import ModelTestCase\nfrom .base import TestModel\n\n\nclass Person(TestModel):\n    name = TextField()\n\n\nclass Relationship(TestModel):\n    from_person = ForeignKeyField(Person, backref='relationships')\n    to_person = ForeignKeyField(Person, backref='related_to')\n\n\nclass Note(TestModel):\n    person = ForeignKeyField(Person, backref='notes')\n    content = TextField()\n\n\nclass NoteItem(TestModel):\n    note = ForeignKeyField(Note, backref='items')\n    content = TextField()\n\n\nclass Like(TestModel):\n    person = ForeignKeyField(Person, backref='likes')\n    note = ForeignKeyField(Note, backref='likes')\n\n\nclass Flag(TestModel):\n    note = ForeignKeyField(Note, backref='flags')\n    is_spam = BooleanField()\n\n\nclass Category(TestModel):\n    name = TextField()\n    parent = ForeignKeyField('self', backref='children', null=True)\n\n\nclass Package(TestModel):\n    barcode = TextField(unique=True)\n\n\nclass PackageItem(TestModel):\n    name = TextField()\n    package = ForeignKeyField(Package, backref='items', field=Package.barcode)\n\n\nclass TestPrefetch(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Person, Note, NoteItem, Like, Flag]\n\n    def create_test_data(self):\n        data = {\n            'huey': (\n                ('meow', ('meow-1', 'meow-2', 'meow-3')),\n                ('purr', ()),\n                ('hiss', ('hiss-1', 'hiss-2'))),\n            'mickey': (\n                ('woof', ()),\n                ('bark', ('bark-1', 'bark-2'))),\n            'zaizee': (),\n        }\n        for name, notes in sorted(data.items()):\n            person = Person.create(name=name)\n            for note, items in notes:\n                note = Note.create(person=person, content=note)\n                for item in items:\n                    NoteItem.create(note=note, content=item)\n\n        Flag.create(note=Note.get(Note.content == 'purr'), is_spam=True)\n        Flag.create(note=Note.get(Note.content == 'woof'), is_spam=True)\n\n        Like.create(note=Note.get(Note.content == 'meow'),\n                    person=Person.get(Person.name == 'mickey'))\n        Like.create(note=Note.get(Note.content == 'woof'),\n                    person=Person.get(Person.name == 'huey'))\n\n    def setUp(self):\n        super(TestPrefetch, self).setUp()\n        self.create_test_data()\n\n    def accumulate_results(self, query, sort_items=False):\n        accum = []\n        for person in query:\n            notes = []\n            for note in person.notes:\n                items = []\n                for item in note.items:\n                    items.append(item.content)\n                if sort_items:\n                    items.sort()\n                notes.append((note.content, items))\n            if sort_items:\n                notes.sort()\n            accum.append((person.name, notes))\n        return accum\n\n    def test_prefetch_simple(self):\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(3):\n                people = Person.select().order_by(Person.name)\n                query = people.prefetch(Note, NoteItem, prefetch_type=pt)\n                accum = self.accumulate_results(query, sort_items=True)\n\n            self.assertEqual(accum, [\n                ('huey', [\n                    ('hiss', ['hiss-1', 'hiss-2']),\n                    ('meow', ['meow-1', 'meow-2', 'meow-3']),\n                    ('purr', [])]),\n                ('mickey', [\n                    ('bark', ['bark-1', 'bark-2']),\n                    ('woof', [])]),\n                ('zaizee', []),\n            ])\n\n    def test_prefetch_filter(self):\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(3):\n                people = Person.select().order_by(Person.name)\n                notes = (Note\n                         .select()\n                         .where(Note.content.not_in(('hiss', 'meow', 'woof')))\n                         .order_by(Note.content.desc()))\n                items = NoteItem.select().where(\n                    ~NoteItem.content.endswith('-2'))\n                query = prefetch(people, notes, items, prefetch_type=pt)\n                self.assertEqual(self.accumulate_results(query), [\n                    ('huey', [('purr', [])]),\n                    ('mickey', [('bark', ['bark-1'])]),\n                    ('zaizee', []),\n                ])\n\n    def test_prefetch_reverse(self):\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(2):\n                people = Person.select().order_by(Person.name)\n                notes = Note.select().order_by(Note.content)\n                query = prefetch(notes, people, prefetch_type=pt)\n                accum = [(note.content, note.person.name) for note in query]\n                self.assertEqual(accum, [\n                    ('bark', 'mickey'),\n                    ('hiss', 'huey'),\n                    ('meow', 'huey'),\n                    ('purr', 'huey'),\n                    ('woof', 'mickey')])\n\n    def test_prefetch_reverse_with_parent_join(self):\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(2):\n                notes = (Note\n                         .select(Note, Person)\n                         .join(Person)\n                         .order_by(Note.content))\n                items = NoteItem.select().order_by(NoteItem.content.desc())\n                query = prefetch(notes, items, prefetch_type=pt)\n                accum = [(note.person.name,\n                          note.content,\n                          [item.content for item in note.items])\n                         for note in query]\n                self.assertEqual(accum, [\n                    ('mickey', 'bark', ['bark-2', 'bark-1']),\n                    ('huey', 'hiss', ['hiss-2', 'hiss-1']),\n                    ('huey', 'meow', ['meow-3', 'meow-2', 'meow-1']),\n                    ('huey', 'purr', []),\n                    ('mickey', 'woof', []),\n                ])\n\n    def test_prefetch_multi_depth(self):\n        for pt in PREFETCH_TYPE.values():\n            people = Person.select().order_by(Person.name)\n            notes = Note.select().order_by(Note.content)\n            items = NoteItem.select().order_by(NoteItem.content)\n            flags = Flag.select().order_by(Flag.id)\n\n            LikePerson = Person.alias('lp')\n            likes = (Like\n                     .select(Like, LikePerson.name)\n                     .join(LikePerson, on=(Like.person == LikePerson.id)))\n\n            # Five queries:\n            # - person (outermost query)\n            # - notes for people\n            # - items for notes\n            # - flags for notes\n            # - likes for notes (includes join to person)\n            with self.assertQueryCount(5):\n                query = prefetch(people, notes, items, flags, likes,\n                                 prefetch_type=pt)\n                accum = []\n                for person in query:\n                    notes = []\n                    for note in person.notes:\n                        items = [item.content for item in note.items]\n                        likes = [like.person.name for like in note.likes]\n                        flags = [flag.is_spam for flag in note.flags]\n                        notes.append((note.content, items, likes, flags))\n                    accum.append((person.name, notes))\n\n            self.assertEqual(accum, [\n                ('huey', [\n                    ('hiss', ['hiss-1', 'hiss-2'], [], []),\n                    ('meow', ['meow-1', 'meow-2', 'meow-3'], ['mickey'], []),\n                    ('purr', [], [], [True])]),\n                ('mickey', [\n                    ('bark', ['bark-1', 'bark-2'], [], []),\n                    ('woof', [], ['huey'], [True])]),\n                (u'zaizee', []),\n            ])\n\n    def test_prefetch_multi_depth_no_join(self):\n        for pt in PREFETCH_TYPE.values():\n            LikePerson = Person.alias()\n            people = Person.select().order_by(Person.name)\n            notes = Note.select().order_by(Note.content)\n            items = NoteItem.select().order_by(NoteItem.content)\n            flags = Flag.select().order_by(Flag.id)\n\n            with self.assertQueryCount(6):\n                query = prefetch(people, notes, items, flags, Like, LikePerson,\n                                 prefetch_type=pt)\n                accum = []\n                for person in query:\n                    notes = []\n                    for note in person.notes:\n                        items = [item.content for item in note.items]\n                        likes = [like.person.name for like in note.likes]\n                        flags = [flag.is_spam for flag in note.flags]\n                        notes.append((note.content, items, likes, flags))\n                    accum.append((person.name, notes))\n\n            self.assertEqual(accum, [\n                ('huey', [\n                    ('hiss', ['hiss-1', 'hiss-2'], [], []),\n                    ('meow', ['meow-1', 'meow-2', 'meow-3'], ['mickey'], []),\n                    ('purr', [], [], [True])]),\n                ('mickey', [\n                    ('bark', ['bark-1', 'bark-2'], [], []),\n                    ('woof', [], ['huey'], [True])]),\n                (u'zaizee', []),\n            ])\n\n    def test_prefetch_with_group_by(self):\n        for pt in PREFETCH_TYPE.values():\n            people = (Person\n                      .select(Person, fn.COUNT(Note.id).alias('note_count'))\n                      .join(Note, JOIN.LEFT_OUTER)\n                      .group_by(Person)\n                      .order_by(Person.name))\n            notes = Note.select().order_by(Note.content)\n            items = NoteItem.select().order_by(NoteItem.content)\n            with self.assertQueryCount(3):\n                query = prefetch(people, notes, items, prefetch_type=pt)\n                self.assertEqual(self.accumulate_results(query), [\n                    ('huey', [\n                        ('hiss', ['hiss-1', 'hiss-2']),\n                        ('meow', ['meow-1', 'meow-2', 'meow-3']),\n                        ('purr', [])]),\n                    ('mickey', [\n                        ('bark', ['bark-1', 'bark-2']),\n                        ('woof', [])]),\n                    ('zaizee', []),\n                ])\n\n                huey, mickey, zaizee = query\n                self.assertEqual(huey.note_count, 3)\n                self.assertEqual(mickey.note_count, 2)\n                self.assertEqual(zaizee.note_count, 0)\n\n    @requires_models(Category)\n    def test_prefetch_self_join(self):\n        def cc(name, parent=None):\n            return Category.create(name=name, parent=parent)\n        root = cc('root')\n        p1 = cc('p1', root)\n        p2 = cc('p2', root)\n        for p in (p1, p2):\n            for i in range(2):\n                cc('%s-%s' % (p.name, i + 1), p)\n\n        for pt in PREFETCH_TYPE.values():\n            Child = Category.alias('child')\n            with self.assertQueryCount(2):\n                query = prefetch(Category.select().order_by(Category.id),\n                                 Child, prefetch_type=pt)\n                names_and_children = [\n                    (cat.name, [child.name for child in cat.children])\n                    for cat in query]\n\n            self.assertEqual(names_and_children, [\n                ('root', ['p1', 'p2']),\n                ('p1', ['p1-1', 'p1-2']),\n                ('p2', ['p2-1', 'p2-2']),\n                ('p1-1', []),\n                ('p1-2', []),\n                ('p2-1', []),\n                ('p2-2', []),\n            ])\n\n    @requires_models(Category)\n    def test_prefetch_adjacency_list(self):\n        def cc(name, parent=None):\n            return Category.create(name=name, parent=parent)\n\n        tree = ('root', (\n            ('n1', (\n                ('c11', ()),\n                ('c12', ()))),\n            ('n2', (\n                ('c21', ()),\n                ('c22', (\n                    ('g221', ()),\n                    ('g222', ()))),\n                ('c23', ()),\n                ('c24', (\n                    ('g241', ()),\n                    ('g242', ()),\n                    ('g243', ())))))))\n        stack = [(None, tree)]\n        while stack:\n            parent, (name, children) = stack.pop()\n            node = cc(name, parent)\n            for child_tree in children:\n                stack.insert(0, (node, child_tree))\n\n        for pt in PREFETCH_TYPE.values():\n            C = Category.alias('c')\n            G = Category.alias('g')\n            GG = Category.alias('gg')\n            GGG = Category.alias('ggg')\n            query = Category.select().where(Category.name == 'root')\n            with self.assertQueryCount(5):\n                pf = prefetch(query, C, (G, C), (GG, G), (GGG, GG),\n                              prefetch_type=pt)\n                def gather(c):\n                    children = sorted([gather(ch) for ch in c.children])\n                    return (c.name, tuple(children))\n                nodes = list(pf)\n                self.assertEqual(len(nodes), 1)\n                pf_tree = gather(nodes[0])\n\n            self.assertEqual(tree, pf_tree)\n\n    def test_prefetch_specific_model(self):\n        # Person -> Note\n        #        -> Like (has fks to both person and note)\n        Like.create(note=Note.get(Note.content == 'woof'),\n                    person=Person.get(Person.name == 'zaizee'))\n        NoteAlias = Note.alias('na')\n\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(3):\n                people = Person.select().order_by(Person.name)\n                notes = Note.select().order_by(Note.content)\n                likes = (Like\n                         .select(Like, NoteAlias.content)\n                         .join(NoteAlias, on=(Like.note == NoteAlias.id))\n                         .order_by(NoteAlias.content))\n                query = prefetch(people, notes, (likes, Person),\n                                 prefetch_type=pt)\n                accum = []\n                for person in query:\n                    likes = []\n                    notes = []\n                    for note in person.notes:\n                        notes.append(note.content)\n                    for like in person.likes:\n                        likes.append(like.note.content)\n                    accum.append((person.name, notes, likes))\n\n            self.assertEqual(accum, [\n                ('huey', ['hiss', 'meow', 'purr'], ['woof']),\n                ('mickey', ['bark', 'woof'], ['meow']),\n                ('zaizee', [], ['woof']),\n            ])\n\n    @requires_models(Relationship)\n    def test_multiple_foreign_keys(self):\n        self.database.pragma('foreign_keys', 0)\n        Person.delete().execute()\n        c, h, z = [Person.create(name=name) for name in\n                                 ('charlie', 'huey', 'zaizee')]\n        RC = lambda f, t: Relationship.create(from_person=f, to_person=t)\n        r1 = RC(c, h)\n        r2 = RC(c, z)\n        r3 = RC(h, c)\n        r4 = RC(z, c)\n\n        def assertRelationships(attr, values):\n            self.assertEqual(len(attr),len(values))\n            for relationship, value in zip(attr, values):\n                self.assertEqual(relationship.__data__, value)\n\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(2):\n                people = Person.select().order_by(Person.name)\n                relationships = Relationship.select().order_by(Relationship.id)\n                query = prefetch(people, relationships, prefetch_type=pt)\n                cp, hp, zp = list(query)\n\n                assertRelationships(cp.relationships, [\n                    {'id': r1.id, 'from_person': c.id, 'to_person': h.id},\n                    {'id': r2.id, 'from_person': c.id, 'to_person': z.id}])\n                assertRelationships(cp.related_to, [\n                    {'id': r3.id, 'from_person': h.id, 'to_person': c.id},\n                    {'id': r4.id, 'from_person': z.id, 'to_person': c.id}])\n\n                assertRelationships(hp.relationships, [\n                    {'id': r3.id, 'from_person': h.id, 'to_person': c.id}])\n                assertRelationships(hp.related_to, [\n                    {'id': r1.id, 'from_person': c.id, 'to_person': h.id}])\n\n                assertRelationships(zp.relationships, [\n                    {'id': r4.id, 'from_person': z.id, 'to_person': c.id}])\n                assertRelationships(zp.related_to, [\n                    {'id': r2.id, 'from_person': c.id, 'to_person': z.id}])\n\n            with self.assertQueryCount(2):\n                query = prefetch(relationships, people, prefetch_type=pt)\n                accum = []\n                for row in query:\n                    accum.append((row.from_person.name, row.to_person.name))\n                self.assertEqual(accum, [\n                    ('charlie', 'huey'),\n                    ('charlie', 'zaizee'),\n                    ('huey', 'charlie'),\n                    ('zaizee', 'charlie')])\n\n        m = Person.create(name='mickey')\n        RC(h, m)\n\n        def assertNames(p, ns):\n            self.assertEqual([r.to_person.name for r in p.relationships], ns)\n\n        # Use prefetch to go Person -> Relationship <- Person (PA).\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(3):\n                people = (Person\n                          .select()\n                          .where(Person.name != 'mickey')\n                          .order_by(Person.name))\n                relationships = Relationship.select().order_by(Relationship.id)\n                PA = Person.alias()\n                query = prefetch(people, relationships, PA, prefetch_type=pt)\n                cp, hp, zp = list(query)\n                assertNames(cp, ['huey', 'zaizee'])\n                assertNames(hp, ['charlie', 'mickey'])\n                assertNames(zp, ['charlie'])\n\n            # User prefetch to go Person -> Relationship+Person (PA).\n            with self.assertQueryCount(2):\n                people = (Person\n                          .select()\n                          .where(Person.name != 'mickey')\n                          .order_by(Person.name))\n                rels = (Relationship\n                        .select(Relationship, PA)\n                        .join(PA, on=(Relationship.to_person == PA.id))\n                        .order_by(Relationship.id))\n                query = prefetch(people, rels, prefetch_type=pt)\n                cp, hp, zp = list(query)\n                assertNames(cp, ['huey', 'zaizee'])\n                assertNames(hp, ['charlie', 'mickey'])\n                assertNames(zp, ['charlie'])\n\n    def test_prefetch_through_manytomany(self):\n        Like.create(note=Note.get(Note.content == 'meow'),\n                    person=Person.get(Person.name == 'zaizee'))\n        Like.create(note=Note.get(Note.content == 'woof'),\n                    person=Person.get(Person.name == 'zaizee'))\n\n        for pt in PREFETCH_TYPE.values():\n            with self.assertQueryCount(3):\n                people = Person.select().order_by(Person.name)\n                notes = Note.select().order_by(Note.content)\n                likes = Like.select().order_by(Like.id)\n                query = prefetch(people, likes, notes, prefetch_type=pt)\n                accum = []\n                for person in query:\n                    liked_notes = []\n                    for like in person.likes:\n                        liked_notes.append(like.note.content)\n                    accum.append((person.name, liked_notes))\n\n            self.assertEqual(accum, [\n                ('huey', ['woof']),\n                ('mickey', ['meow']),\n                ('zaizee', ['meow', 'woof']),\n            ])\n\n    @requires_models(Package, PackageItem)\n    def test_prefetch_non_pk_fk(self):\n        data = (\n            ('101', ('a', 'b')),\n            ('102', ('a', 'b')),\n            ('103', ()),\n            ('104', ('a', 'b', 'c', 'd', 'e')),\n        )\n        for barcode, items in data:\n            Package.create(barcode=barcode)\n            for item in items:\n                PackageItem.create(package=barcode, name=item)\n\n        for pt in PREFETCH_TYPE.values():\n            packages = Package.select().order_by(Package.barcode)\n            items = PackageItem.select().order_by(PackageItem.name)\n\n            with self.assertQueryCount(2):\n                query = prefetch(packages, items, prefetch_type=pt)\n                for package, (barcode, items) in zip(query, data):\n                    self.assertEqual(package.barcode, barcode)\n                    self.assertEqual([item.name for item in package.items],\n                                     list(items))\n\n    def test_prefetch_mark_dirty_regression(self):\n        for pt in PREFETCH_TYPE.values():\n            people = Person.select().order_by(Person.name)\n            query = people.prefetch(Note, NoteItem, prefetch_type=pt)\n            for person in query:\n                self.assertEqual(person.dirty_fields, [])\n                for note in person.notes:\n                    self.assertEqual(note.dirty_fields, [])\n                    for item in note.items:\n                        self.assertEqual(item.dirty_fields, [])\n\n\n\nclass X(TestModel):\n    name = TextField()\nclass Z(TestModel):\n    x = ForeignKeyField(X)\n    name = TextField()\n\nclass A(TestModel):\n    name = TextField()\n    x = ForeignKeyField(X)\nclass B(TestModel):\n    name = TextField()\n    a = ForeignKeyField(A)\n    x = ForeignKeyField(X)\nclass C(TestModel):\n    name = TextField()\n    b = ForeignKeyField(B)\n    x = ForeignKeyField(X, null=True)\n\nclass C1(TestModel):\n    name = TextField()\n    c = ForeignKeyField(C)\nclass C2(TestModel):\n    name = TextField()\n    c = ForeignKeyField(C)\n\n\nclass TestPrefetchMultiRefs(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [X, Z, A, B, C, C1, C2]\n\n    def test_prefetch_multirefs(self):\n        x1, x2, x3 = [X.create(name=n) for n in ('x1', 'x2', 'x3')]\n        for i, x in enumerate((x1, x2, x3), 1):\n            for j in range(i):\n                Z.create(x=x, name='%s-z%s' % (x.name, j))\n\n        xs = {x.name: x for x in X.select()}\n        xs[None] = None\n\n        data = [\n            ('a1',\n             'x1',\n             ['x1-z0'],\n             [\n                 ('a1-b1', 'x1', ['x1-z0'], [\n                     ('a1-b1-c1', 'x1', ['x1-z0'], [], []),\n                 ]),\n             ]),\n            ('a2',\n             'x2',\n             ['x2-z0', 'x2-z1'],\n             [\n                 ('a2-b1', 'x1', ['x1-z0'], [\n                     ('a2-b1-c1', 'x1', ['x1-z0'], [], []),\n                 ]),\n                 ('a2-b2', 'x2', ['x2-z0', 'x2-z1'], [\n                     ('a2-b2-c1', 'x2', ['x2-z0', 'x2-z1'], [], []),\n                     ('a2-b2-c2', 'x1', ['x1-z0'], [], []),\n                     ('a2-b2-cx', None, [], [], []),\n                 ]),\n             ]),\n            ('a3',\n             'x3',\n             ['x3-z0', 'x3-z1', 'x3-z2'],\n             [\n                 ('a3-b1', 'x1', ['x1-z0'], [\n                     ('a3-b1-c1', 'x1', ['x1-z0'], [], []),\n                 ]),\n                 ('a3-b2', 'x2', ['x2-z0', 'x2-z1'], [\n                     ('a3-b2-c1', 'x2', ['x2-z0', 'x2-z1'], [], []),\n                     ('a3-b2-c2', 'x2', ['x2-z0', 'x2-z1'], [], []),\n                     ('a3-b2-cx1', None, [], [], []),\n                     ('a3-b2-cx2', None, [], [], []),\n                     ('a3-b2-cx3', None, [], [], []),\n                 ]),\n                 ('a3-b3', 'x3', ['x3-z0', 'x3-z1', 'x3-z2'], [\n                     ('a3-b3-c1', 'x3', ['x3-z0', 'x3-z1', 'x3-z2'], [], []),\n                     ('a3-b3-c2', 'x3', ['x3-z0', 'x3-z1', 'x3-z2'], [], []),\n                     ('a3-b3-c3', 'x3', ['x3-z0', 'x3-z1', 'x3-z2'],\n                      ['c1-1', 'c1-2', 'c1-3', 'c1-4'],\n                      ['c2-1', 'c2-2']),\n                 ]),\n             ]),\n        ]\n\n        for a, ax, azs, bs in data:\n            a = A.create(name=a, x=xs[ax])\n            for b, bx, bzs, cs in bs:\n                b = B.create(name=b, a=a, x=xs[bx])\n                for c, cx, czs, c1s, c2s in cs:\n                    c = C.create(name=c, b=b, x=xs[cx])\n                    for c1 in c1s:\n                        C1.create(name=c1, c=c)\n                    for c2 in c2s:\n                        C2.create(name=c2, c=c)\n\n\n        AX = X.alias('ax')\n        AXZ = Z.alias('axz')\n        BX = X.alias('bx')\n        BXZ = Z.alias('bxz')\n        CX = X.alias('cx')\n        CXZ = Z.alias('cxz')\n\n        with self.assertQueryCount(11):\n            q = prefetch(A.select().order_by(A.name), *(\n                (AX, A), (AXZ, AX),\n                (B, A), (BX, B), (BXZ, BX),\n                (C, B), (CX, C), (CXZ, CX),\n                (C1, C), (C2, C)))\n\n        with self.assertQueryCount(0):\n            accum = []\n            for a in list(q):\n                azs = [z.name for z in a.x.z_set]\n                bs = []\n                for b in a.b_set:\n                    bzs = [z.name for z in b.x.z_set]\n                    cs = []\n                    for c in b.c_set:\n                        czs = [z.name for z in c.x.z_set] if c.x else []\n                        c1s = [c1.name for c1 in c.c1_set]\n                        c2s = [c2.name for c2 in c.c2_set]\n                        cs.append((c.name, c.x.name if c.x else None, czs,\n                                   c1s, c2s))\n\n                    bs.append((b.name, b.x.name, bzs, cs))\n                accum.append((a.name, a.x.name, azs, bs))\n\n        self.assertEqual(data, accum)\n"
  },
  {
    "path": "tests/pwasyncio.py",
    "content": "import asyncio\nimport collections\nimport contextvars\nimport glob\nimport itertools\nimport tempfile\nimport os\nimport unittest\nfrom unittest.mock import Mock, AsyncMock, MagicMock, patch\n\nfrom peewee import *\nfrom playhouse.pwasyncio import *\nfrom playhouse.pwasyncio import _State, _ConnectionState, _lazy_cursor_iter\nfrom .base import MYSQL_PARAMS\nfrom .base import PSQL_PARAMS\nfrom .base import IS_MYSQL\nfrom .base import IS_POSTGRESQL\n\ntry:\n    import asyncpg\nexcept ImportError:\n    asyncpg = None\n\ntry:\n    import aiomysql\nexcept ImportError:\n    aiomysql = None\n\nimport aiosqlite\n\nSQLITE_RETURNING = aiosqlite.sqlite_version_info >= (3, 35, 0)\n\n\nclass TestModel(Model):\n    name = CharField()\n    value = IntegerField(default=0)\n\nclass User(Model):\n    username = CharField()\n\nclass Tweet(Model):\n    user = ForeignKeyField(User, backref='tweets')\n    message = TextField()\n\n\nclass TestGreenletSpawn(unittest.IsolatedAsyncioTestCase):\n    async def test_simple_function(self):\n        result = await greenlet_spawn(lambda x, y: x + y, 5, 3)\n        self.assertEqual(result, 8)\n\n    async def test_function_with_await(self):\n        async def async_helper():\n            await asyncio.sleep(0.01)\n            return 2\n        def func():\n            return await_(async_helper()) * 2\n        self.assertEqual(await greenlet_spawn(func), 4)\n\n    async def test_multiple_awaits(self):\n        async def fetch_value(val):\n            await asyncio.sleep(0.01)\n            return val\n        def multi():\n            return sum([await_(fetch_value(i)) for i in [10, 20, 30]])\n        self.assertEqual(await greenlet_spawn(multi), 60)\n\n    async def test_exception_propagation(self):\n        with self.assertRaises(ValueError):\n            await greenlet_spawn(lambda: (_ for _ in ()).throw(ValueError('x')))\n\n    async def test_exception_in_awaitable(self):\n        async def fail():\n            raise RuntimeError('async error')\n        with self.assertRaises(RuntimeError):\n            await greenlet_spawn(lambda: await_(fail()))\n\n    def test_await_outside_greenlet(self):\n        with self.assertRaises(MissingGreenletBridge):\n            await_(Mock())\n\n    async def test_contextvars(self):\n        var = contextvars.ContextVar('data', default='x')\n        state = []\n        def get_var():\n            state.append(var.get())\n        async def aget_var():\n            await greenlet_spawn(get_var)\n\n        var.set('y')\n        await aget_var()\n        await greenlet_spawn(lambda: await_(aget_var()))\n        await aget_var()\n        self.assertEqual(state, ['y', 'y', 'y'])\n\n\nclass TestConnectionState(unittest.IsolatedAsyncioTestCase):\n    async def test_task_isolation(self):\n        cs = _ConnectionState()\n        async def worker(tid):\n            cs._current().conn = tid\n            await asyncio.sleep(0.01)\n            return cs._current().conn\n        results = await asyncio.gather(*[worker(i) for i in range(5)])\n        self.assertEqual(results, [0, 1, 2, 3, 4])\n\n    async def test_state_attributes(self):\n        cs = _ConnectionState()\n        cs.set_connection('c')\n        cs.transactions.append(1)\n        self.assertEqual(cs.conn, 'c')\n        self.assertFalse(cs.closed)\n        self.assertEqual(cs.transactions, [1])\n\n    async def test_get_returns_fresh_state(self):\n        s = _ConnectionState()._current()\n        self.assertIsNone(s.conn)\n        self.assertTrue(s.closed)\n        self.assertEqual(s.transactions, [])\n\n    async def test_reset(self):\n        cs = _ConnectionState()\n        cs.set_connection('x')\n        cs.transactions.append(1)\n        cs.reset()\n        self.assertIsNone(cs.conn)\n        self.assertTrue(cs.closed)\n        self.assertEqual(cs.transactions, [])\n\n    async def test_set_connection(self):\n        cs = _ConnectionState()\n        m = Mock()\n        cs.set_connection(m)\n        self.assertIs(cs.conn, m)\n        self.assertFalse(cs.closed)\n\n    async def test_done_callback_orphans_connection(self):\n        cs = _ConnectionState()\n        conn_mock = Mock()\n\n        async def acquire_and_abandon():\n            cs.set_connection(conn_mock)\n            return id(asyncio.current_task())\n\n        task_id = await asyncio.create_task(acquire_and_abandon())\n        # After the task completes, the done-callback should have fired.\n        await asyncio.sleep(0)\n        self.assertNotIn(task_id, cs._states)\n        self.assertIn(conn_mock, cs._orphaned_conns)\n\n    async def test_done_callback_noop_when_closed(self):\n        cs = _ConnectionState()\n\n        async def open_and_close():\n            cs.set_connection(Mock())\n            cs.reset()  # Simulate proper close.\n\n        await asyncio.create_task(open_and_close())\n        await asyncio.sleep(0)\n        self.assertEqual(cs._orphaned_conns, [])\n\n\ndef _make_lazy_cursor(rows, batch_size=2):\n    it = iter(rows)\n    fetch_counts = []\n    cleanup_called = []\n\n    async def fetch_many(count):\n        fetch_counts.append(count)\n        return list(itertools.islice(it, count))\n\n    async def cleanup():\n        cleanup_called.append(True)\n\n    cursor = CursorAdapter(\n        description=[('id',), ('name',)],\n        fetch_many=fetch_many,\n        cleanup=cleanup,\n        buffer_size=batch_size)\n    return cursor, fetch_counts, cleanup_called\n\n\nclass TestCursorAdapter(unittest.IsolatedAsyncioTestCase):\n    def test_eager_fetchone(self):\n        c = CursorAdapter([(1, 'a'), (2, 'b'), (3, 'c')])\n        self.assertEqual(c.fetchone(), (1, 'a'))\n        self.assertEqual(c.fetchone(), (2, 'b'))\n        self.assertEqual(c.fetchone(), (3, 'c'))\n        self.assertIsNone(c.fetchone())\n\n    def test_eager_fetchall(self):\n        rows = [(1,), (2,)]\n        c = CursorAdapter(rows)\n        self.assertIs(c.fetchall(), rows)\n\n    def test_eager_iter(self):\n        rows = [(1,), (2,), (3,)]\n        self.assertEqual(list(CursorAdapter(rows)), rows)\n        self.assertEqual(CursorAdapter(rows).rowcount, 3)\n\n    def test_eager_metadata(self):\n        c = CursorAdapter()\n        self.assertEqual(c._rows, [])\n        self.assertEqual(c.rowcount, 0)\n        self.assertEqual(c.description, [])\n        self.assertIsNone(c.fetchone())\n        self.assertEqual(list(c), [])\n\n        c = CursorAdapter([(1,)], lastrowid=5, rowcount=1,\n                          description=[('id',)])\n        self.assertEqual(c.lastrowid, 5)\n        self.assertEqual(c.rowcount, 1)\n        self.assertEqual(c.description, [('id',)])\n\n    async def test_lazy_fetchone_batches(self):\n        rows = [(i,) for i in range(5)]\n        cursor, counts, _ = _make_lazy_cursor(rows, batch_size=2)\n\n        collected = []\n        def drain():\n            while True:\n                r = cursor.fetchone()\n                if r is None:\n                    break\n                collected.append(r)\n        await greenlet_spawn(drain)\n\n        self.assertEqual(collected, rows)\n        # 2 + 2 + 1 + 0(empty) = 4 calls, each requesting 2\n        self.assertEqual(len(counts), 4)\n        self.assertTrue(all(c == 2 for c in counts))\n\n    async def test_lazy_fetchone_empty(self):\n        cursor, _, _ = _make_lazy_cursor([], batch_size=2)\n        self.assertIsNone(await greenlet_spawn(cursor.fetchone))\n        # Already exhausted, still returns None.\n        self.assertIsNone(await greenlet_spawn(cursor.fetchone))\n\n    async def test_lazy_iter(self):\n        rows = [(i,) for i in range(7)]\n        cursor, counts, _ = _make_lazy_cursor(rows, batch_size=3)\n        self.assertEqual(await greenlet_spawn(list, cursor), rows)\n        # 3 + 3 + 1 + 0 = 4 calls\n        self.assertEqual(len(counts), 4)\n\n    async def test_lazy_fetchall(self):\n        rows = [(1,), (2,), (3,)]\n        cursor, _, _ = _make_lazy_cursor(rows, batch_size=10)\n        self.assertEqual(await greenlet_spawn(cursor.fetchall), rows)\n\n    async def test_lazy_buffer_reuse(self):\n        rows = [(i,) for i in range(3)]\n        cursor, counts, _ = _make_lazy_cursor(rows, batch_size=10)\n        await greenlet_spawn(cursor.fetchone)\n        self.assertEqual(len(counts), 1)\n        await greenlet_spawn(cursor.fetchone)\n        await greenlet_spawn(cursor.fetchone)\n        self.assertEqual(len(counts), 1)  # still 1\n        await greenlet_spawn(cursor.fetchone)  # second fetch (empty).\n        self.assertEqual(len(counts), 2)\n\n    async def test_lazy_description(self):\n        cursor, _, _ = _make_lazy_cursor([], batch_size=2)\n        self.assertEqual(cursor.description, [('id',), ('name',)])\n\n    async def test_lazy_buffer_size_override(self):\n        rows = [(i,) for i in range(10)]\n        cursor, counts, _ = _make_lazy_cursor(rows, batch_size=5)\n        cursor._buffer_size = 3\n        await greenlet_spawn(list, cursor)\n        self.assertTrue(all(c == 3 for c in counts))\n\n    async def test_aclose_cleanup(self):\n        cursor, _, cleanup = _make_lazy_cursor([], batch_size=2)\n        await cursor.aclose()\n        self.assertEqual(cleanup, [True])\n        self.assertIsNone(cursor._fetch_many)\n        self.assertIsNone(cursor._cleanup)\n\n    async def test_aclose_idempotent(self):\n        call_count = []\n        async def cleanup():\n            call_count.append(1)\n        cursor, _, _ = _make_lazy_cursor([], batch_size=2)\n        cursor._cleanup = cleanup\n        await cursor.aclose()\n        await cursor.aclose()\n        self.assertEqual(len(call_count), 1)\n\n    async def test_aclose_noop_for_eager(self):\n        await CursorAdapter([(1,)]).aclose()  # must not raise\n\n    async def test_lazy_cursor_iter(self):\n        rows = [(1,), (2,), (3,)]\n        cursor, _, _ = _make_lazy_cursor(rows, batch_size=10)\n        result = await greenlet_spawn(list, _lazy_cursor_iter(cursor))\n        self.assertEqual(result, rows)\n\n    async def test_lazy_cursor_iter_empty(self):\n        cursor, _, _ = _make_lazy_cursor([], batch_size=2)\n        result = await greenlet_spawn(list, _lazy_cursor_iter(cursor))\n        self.assertEqual(result, [])\n\n\nclass TestConnectionWrappers(unittest.IsolatedAsyncioTestCase):\n    async def test_sqlite_execute(self):\n        mock_cursor = AsyncMock()\n        mock_cursor.fetchall.return_value = [(1, 'test')]\n        mock_cursor.lastrowid = 1\n        mock_cursor.rowcount = 1\n        mock_cursor.description = [('id',), ('name',)]\n        mock_conn = AsyncMock()\n        mock_conn.execute.return_value = mock_cursor\n\n        result = await AsyncSqliteConnection(mock_conn).execute(\n            'SELECT * FROM test')\n        self.assertIsInstance(result, CursorAdapter)\n        self.assertEqual(result.fetchall(), [(1, 'test')])\n        self.assertEqual(result.lastrowid, 1)\n        mock_cursor.close.assert_awaited_once()\n\n    async def test_sqlite_execute_iter_returns_lazy(self):\n        mock_cursor = AsyncMock()\n        mock_cursor.description = [('a',), ('b',)]\n        mock_conn = AsyncMock()\n        mock_conn.execute.return_value = mock_cursor\n\n        conn = AsyncSqliteConnection(mock_conn)\n        cursor = await conn.execute_iter('SELECT a, b FROM t')\n        self.assertIsInstance(cursor, CursorAdapter)\n        self.assertIsNotNone(cursor._fetch_many)\n        self.assertEqual(cursor.description, [('a',), ('b',)])\n        await cursor.aclose()\n\n    async def test_sqlite_execute_iter_lock_lifecycle(self):\n        mock_cursor = AsyncMock()\n        mock_cursor.description = []\n        mock_conn = AsyncMock()\n        mock_conn.execute.return_value = mock_cursor\n\n        conn = AsyncSqliteConnection(mock_conn)\n        cursor = await conn.execute_iter('SELECT 1')\n        self.assertTrue(conn._lock.locked())\n        await cursor.aclose()\n        self.assertFalse(conn._lock.locked())\n        mock_cursor.close.assert_awaited_once()\n\n    async def test_sqlite_execute_iter_lock_on_failure(self):\n        mock_conn = AsyncMock()\n        mock_conn.execute.side_effect = RuntimeError('fail')\n        conn = AsyncSqliteConnection(mock_conn)\n        with self.assertRaises(RuntimeError):\n            await conn.execute_iter('invalid')\n        self.assertFalse(conn._lock.locked())\n\n    async def test_mysql_execute(self):\n        mock_cursor = AsyncMock()\n        mock_cursor.fetchall.return_value = [(1, 'test')]\n        mock_cursor.lastrowid = 1\n        mock_cursor.rowcount = 1\n        mock_cursor.description = [('id',), ('name',)]\n        mock_conn = AsyncMock()\n        mock_conn.cursor.return_value = mock_cursor\n\n        result = await AsyncMySQLConnection(mock_conn).execute(\n            'SELECT * FROM test')\n        self.assertIsInstance(result, CursorAdapter)\n        self.assertEqual(result.fetchall(), [(1, 'test')])\n        mock_cursor.close.assert_awaited_once()\n\n    async def test_mysql_cursor_closed_on_error(self):\n        mock_cursor = AsyncMock()\n        mock_cursor.execute.side_effect = RuntimeError('fail')\n        mock_conn = AsyncMock()\n        mock_conn.cursor.return_value = mock_cursor\n\n        with self.assertRaises(RuntimeError):\n            await AsyncMySQLConnection(mock_conn).execute('invalid')\n        mock_cursor.close.assert_awaited_once()\n\n    async def test_mysql_concurrent_serialized(self):\n        order = []\n        async def tracked(sql, params):\n            order.append(f'start-{sql}')\n            await asyncio.sleep(0.05)\n            order.append(f'end-{sql}')\n            return []\n        mock_cursor = AsyncMock()\n        mock_cursor.execute = tracked\n        mock_conn = AsyncMock()\n        mock_conn.cursor.return_value = mock_cursor\n\n        conn = AsyncMySQLConnection(mock_conn)\n        await asyncio.gather(conn.execute('Q1', None),\n                             conn.execute('Q2', None))\n        idx = {e: i for i, e in enumerate(order)}\n        self.assertTrue(idx['end-Q1'] < idx['start-Q2']\n                        or idx['end-Q2'] < idx['start-Q1'])\n\n    async def test_mysql_execute_iter_uses_ss_cursor(self):\n        import playhouse.pwasyncio as mod\n        mock_cursor = AsyncMock()\n        mock_cursor.description = [('x',)]\n        mock_cursor.execute = AsyncMock()\n        mock_conn = AsyncMock()\n        mock_conn.cursor.return_value = mock_cursor\n\n        sentinel = object()\n        with patch.object(mod, 'aiomysql') as m:\n            m.SSCursor = sentinel\n            cursor = await AsyncMySQLConnection(mock_conn).execute_iter(\n                'SELECT 1')\n        mock_conn.cursor.assert_awaited_once_with(sentinel)\n        self.assertIsNotNone(cursor._fetch_many)\n        await cursor.aclose()\n\n    async def test_mysql_execute_iter_lock_lifecycle(self):\n        import playhouse.pwasyncio as mod\n        mock_cursor = AsyncMock()\n        mock_cursor.description = [('x',)]\n        mock_cursor.execute = AsyncMock()\n        mock_cursor.close = AsyncMock()\n        mock_conn = AsyncMock()\n        mock_conn.cursor.return_value = mock_cursor\n\n        conn = AsyncMySQLConnection(mock_conn)\n        with patch.object(mod, 'aiomysql', create=True) as m:\n            m.SSCursor = object()\n            cursor = await conn.execute_iter('SELECT 1')\n        self.assertTrue(conn._lock.locked())\n        await cursor.aclose()\n        self.assertFalse(conn._lock.locked())\n        mock_cursor.close.assert_awaited_once()\n\n    async def test_pg_parameter_conversion(self):\n        mock_record = Mock()\n        mock_record.keys.return_value = ['id', 'name']\n        mock_conn = AsyncMock()\n        mock_conn.fetch.return_value = [mock_record]\n\n        await AsyncPostgresqlConnection(mock_conn).execute(\n            'SELECT * FROM t WHERE id = %s AND name = %s', (1, 'x'))\n        sql = mock_conn.fetch.call_args[0][0]\n        self.assertEqual(sql, 'SELECT * FROM t WHERE id = $1 AND name = $2')\n\n    async def test_pg_concurrent_serialized(self):\n        order = []\n        async def tracked(sql, params=None):\n            order.append(f'start-{sql}')\n            await asyncio.sleep(0.05)\n            order.append(f'end-{sql}')\n            return []\n        mock_conn = AsyncMock()\n        mock_conn.fetch = tracked\n\n        conn = AsyncPostgresqlConnection(mock_conn)\n        await asyncio.gather(conn.execute('Q1', None),\n                             conn.execute('Q2', None))\n        idx = {e: i for i, e in enumerate(order)}\n        self.assertTrue(idx['end-Q1'] < idx['start-Q2']\n                        or idx['end-Q2'] < idx['start-Q1'])\n\n    async def test_pg_no_params(self):\n        mock_conn = AsyncMock()\n        mock_conn.fetch.return_value = []\n        await AsyncPostgresqlConnection(mock_conn).execute(\n            'SELECT * FROM t', None)\n        mock_conn.fetch.assert_called_once_with('SELECT * FROM t')\n\n    async def test_pg_empty_results(self):\n        mock_conn = AsyncMock()\n        mock_conn.fetch.return_value = []\n        r = await AsyncPostgresqlConnection(mock_conn).execute(\n            'SELECT * FROM empty')\n        self.assertEqual(r.fetchall(), [])\n        self.assertEqual(r.description, [])\n\n    def test_translate_placeholders(self):\n        f = AsyncPostgresqlConnection._translate_placeholders\n        self.assertEqual(f('SELECT 1'), 'SELECT 1')\n        self.assertEqual(\n            f('SELECT * FROM t WHERE a = %s AND b = %s'),\n            'SELECT * FROM t WHERE a = $1 AND b = $2')\n        self.assertEqual(\n            f('INSERT INTO t VALUES (%s, %s, %s)'),\n            'INSERT INTO t VALUES ($1, $2, $3)')\n\n    def _pg_mocks(self, rows=None):\n        rows = rows or []\n        attr = MagicMock(); attr.name = 'col1'\n        it = iter(rows)\n        mock_cursor = AsyncMock()\n        async def _fetch(count):\n            return list(itertools.islice(it, count))\n        mock_cursor.fetch = _fetch\n\n        mock_stmt = AsyncMock()\n        mock_stmt.cursor.return_value = mock_cursor\n        mock_stmt.get_attributes = MagicMock(return_value=[attr])\n\n        mock_tr = AsyncMock()\n        mock_conn = MagicMock()\n        mock_conn.transaction.return_value = mock_tr\n        mock_conn.prepare = AsyncMock(return_value=mock_stmt)\n        return mock_conn, mock_tr, mock_stmt, mock_cursor\n\n    async def test_pg_execute_iter_description(self):\n        mock_conn, _, mock_stmt, _ = self._pg_mocks()\n        a1, a2 = MagicMock(), MagicMock()\n        a1.name = 'id'; a2.name = 'username'\n        mock_stmt.get_attributes.return_value = [a1, a2]\n\n        conn = AsyncPostgresqlConnection(mock_conn)\n        cursor = await conn.execute_iter('SELECT id, username FROM users')\n        self.assertEqual(cursor.description, [('id',), ('username',)])\n        await cursor.aclose()\n\n    async def test_pg_execute_iter_starts_transaction(self):\n        mock_conn, mock_tr, _, _ = self._pg_mocks()\n        conn = AsyncPostgresqlConnection(mock_conn)\n        cursor = await conn.execute_iter('SELECT 1')\n        mock_conn.transaction.assert_called_once()\n        mock_tr.start.assert_awaited_once()\n        await cursor.aclose()\n\n    async def test_pg_execute_iter_cleanup_rolls_back(self):\n        mock_conn, mock_tr, _, _ = self._pg_mocks()\n        conn = AsyncPostgresqlConnection(mock_conn)\n        cursor = await conn.execute_iter('SELECT 1')\n        self.assertTrue(conn._lock.locked())\n        await cursor.aclose()\n        mock_tr.rollback.assert_awaited_once()\n        self.assertFalse(conn._lock.locked())\n\n    async def test_pg_execute_iter_translates_placeholders(self):\n        mock_conn, _, _, _ = self._pg_mocks()\n        conn = AsyncPostgresqlConnection(mock_conn)\n        cursor = await conn.execute_iter(\n            'SELECT * FROM t WHERE a = %s AND b = %s', params=(1, 2))\n        sql = mock_conn.prepare.call_args[0][0]\n        self.assertIn('$1', sql)\n        self.assertNotIn('%s', sql)\n        await cursor.aclose()\n\n    async def test_pg_execute_iter_lock_on_failure(self):\n        mock_conn, _, _, _ = self._pg_mocks()\n        mock_conn.prepare = AsyncMock(side_effect=RuntimeError('fail'))\n        conn = AsyncPostgresqlConnection(mock_conn)\n        with self.assertRaises(RuntimeError):\n            await conn.execute_iter('invalid')\n        self.assertFalse(conn._lock.locked())\n\n\nclass TestTaskLifecycle(unittest.IsolatedAsyncioTestCase):\n    async def asyncSetUp(self):\n        with tempfile.NamedTemporaryFile(delete=False) as f:\n            self.db_path = f.name\n\n        self.db = AsyncSqliteDatabase(self.db_path)\n        TestModel._meta.set_database(self.db)\n\n        async with self.db:\n            await self.db.acreate_tables([TestModel])\n\n    async def asyncTearDown(self):\n        await self.db.close_pool()\n\n        if self.db_path and os.path.exists(self.db_path):\n            for fname in glob.glob(self.db_path + '*'):\n                os.unlink(fname)\n\n    async def test_task_id_behavior(self):\n        async def a1(db):\n            accum = []\n            async with db:\n                accum.append(db._state._current())\n                accum.append(await a2(db))\n            return accum\n\n        async def a2(db):\n            async with db:\n                return db._state._current()\n\n        def s1(db):\n            accum = []\n            with db.connection_context():\n                accum.append(db._state._current())\n                accum.append(s2(db))\n            return accum\n\n        def s2(db):\n            with db.connection_context():\n                return db._state._current()\n\n        async with self.db:\n            ids = await(a1(self.db))\n            ids.extend(await self.db.run(s1, self.db))\n\n        self.assertEqual(len(ids), 4)\n        self.assertEqual(len(set(ids)), 1)\n\n        async with self.db:\n            ids = await asyncio.create_task(a1(self.db))\n            ids.extend(await asyncio.create_task(self.db.run(s1, self.db)))\n\n        self.assertEqual(len(ids), 4)\n        self.assertEqual(len(set(ids)), 2)\n\n    async def test_task_state_cleanup_after_completion(self):\n        async def task_with_state():\n            async with self.db:\n                await self.db.run(TestModel.create, name='test', value=1)\n            return id(asyncio.current_task())\n\n        await asyncio.create_task(task_with_state())\n\n        # Child task properly closed connection (async with db), so close pool\n        # should exit cleanly.\n        await asyncio.wait_for(self.db.close_pool(), timeout=2.0)\n\n        # Verify the write persisted.\n        async with self.db:\n            self.assertEqual(await self.db.count(TestModel.select()), 1)\n\n    async def test_concurrent_task_state_isolation(self):\n        async def capture(tid):\n            async with self.db:\n                before = id(self.db._state._current())\n                await self.db.run(TestModel.create, name=f't{tid}', value=tid)\n                after = id(self.db._state._current())\n                self.assertEqual(before, after)\n                return before\n\n        results = await asyncio.gather(*[capture(i) for i in range(5)])\n        self.assertTrue(all(results))\n        self.assertEqual(len(set(results)), 5)\n\n    async def test_connection_returned_when_task_dies(self):\n        async def acquire_and_abandon():\n            await self.db.aconnect()\n            return  # Connection is not closed, callback must handle cleanup.\n\n        await asyncio.create_task(acquire_and_abandon())\n\n        # The done-callback should have moved the connection to orphaned\n        # connections, which are handled either via done callback or during\n        # pool shutdown.\n        await asyncio.wait_for(self.db.close_pool(), timeout=2.0)\n\n\nclass IntegrationTests(object):\n    db_path = None\n    models = [TestModel, User, Tweet]\n\n    def get_database(self):\n        with tempfile.NamedTemporaryFile(delete=False) as f:\n            self.db_path = f.name\n        return AsyncSqliteDatabase(self.db_path)\n\n    def tearDown(self):\n        if self.db_path and os.path.exists(self.db_path):\n            os.unlink(self.db_path)\n\n    async def asyncSetUp(self):\n        try:\n            self.db = self.get_database()\n            await self.db.aconnect()\n            await self.db.aclose()\n        except Exception as exc:\n            self.skipTest(f'Cannot connect: {exc}')\n\n        if isinstance(self.db, AsyncSqliteDatabase):\n            self.driver = 'sqlite'\n            self.support_returning = SQLITE_RETURNING\n        elif isinstance(self.db, AsyncMySQLDatabase):\n            self.driver = 'mysql'\n            self.support_returning = False\n        elif isinstance(self.db, AsyncPostgresqlDatabase):\n            self.driver = 'postgresql'\n            self.support_returning = True\n        else:\n            raise ValueError('Unrecognized driver')\n\n        for m in self.models:\n            m._meta.set_database(self.db)\n        async with self.db:\n            await self.db.adrop_tables(self.models)\n            await self.db.acreate_tables(self.models)\n\n    async def asyncTearDown(self):\n        await self.db.aclose()\n        async with self.db:\n            await self.db.adrop_tables(self.models)\n        await self.db.close_pool()\n\n    async def create_record(self, name='test', value=1):\n        return await self.db.run(TestModel.create, name=name, value=value)\n\n    async def assertCount(self, expected):\n        count = await self.db.run(TestModel.select().count)\n        self.assertEqual(count, expected)\n\n    async def assertNames(self, expected):\n        curs = await self.db.list(TestModel.select().order_by(TestModel.name))\n        self.assertEqual([tm.name for tm in curs], expected)\n\n    async def seed(self, n=20):\n        def _seed():\n            with self.db.atomic():\n                for i in range(n):\n                    TestModel.create(name=f'item{i:02d}', value=i * 10)\n        await self.db.run(_seed)\n\n    async def test_pool_created_on_connect(self):\n        await self.db.aclose()\n        await self.db.close_pool()\n        self.assertIsNone(self.db._pool)\n        await self.db.aconnect()\n        self.assertIsNotNone(self.db._pool)\n        self.assertIsNotNone(self.db._state.conn)\n        self.assertFalse(self.db.is_closed())\n        await self.db.aclose()\n        self.assertIsNone(self.db._state.conn)\n        self.assertTrue(self.db.is_closed())\n\n    async def test_is_closed(self):\n        for i in range(2):\n            await self.db.aconnect()\n            self.assertFalse(self.db.is_closed())\n            await self.db.aclose()\n            self.assertTrue(self.db.is_closed())\n\n    async def test_multiple_close_safe(self):\n        await self.db.aclose()\n        self.assertTrue(self.db.is_closed())\n        await self.db.aclose()\n        await self.db.aconnect()\n        self.assertFalse(self.db.is_closed())\n\n    async def test_reconnect_after_pool_close(self):\n        await self.create_record('first', 1)\n        await self.db.aclose()\n        await self.db.close_pool()\n        self.assertIsNone(self.db._pool)\n        async with self.db:\n            await self.assertCount(1)\n        self.assertIsNotNone(self.db._pool)\n        self.assertTrue(self.db.is_closed())\n\n    async def test_connection_reuse_within_task(self):\n        await self.db.aconnect()\n        c1 = self.db._state.conn\n        await self.create_record('a', 1)\n        c2 = self.db._state.conn\n        await self.create_record('b', 2)\n        self.assertIs(c1, c2)\n        self.assertIs(c2, self.db._state.conn)\n\n    async def test_closing_flag_prevents_connect(self):\n        self.db._closing = True\n        try:\n            with self.assertRaises(InterfaceError):\n                await self.db.aconnect()\n        finally:\n            self.db._closing = False\n\n    async def test_double_close_pool(self):\n        await self.db.aclose()\n        await self.db.close_pool()\n        await self.db.close_pool()\n\n    async def test_dead_connection_replaced(self):\n        if self.driver == 'mysql':\n            self.skipTest('closing underlying conn incompatible with aiomysql')\n            return\n\n        await self.db.aconnect()\n        conn = self.db._state.conn\n        await conn.close()\n        await self.create_record('test', 1)\n        self.assertIsNot(self.db._state.conn, conn)\n        await self.assertCount(1)\n\n    async def test_context_manager(self):\n        async with self.db:\n            self.assertIsNotNone(self.db._state.conn)\n            self.assertFalse(self.db._state.closed)\n            self.assertFalse(self.db.is_closed())\n        self.assertIsNone(self.db._state.conn)\n        self.assertTrue(self.db._state.closed)\n        self.assertTrue(self.db.is_closed())\n\n    async def test_exception_in_context_manager(self):\n        try:\n            async with self.db:\n                raise RuntimeError('fail')\n        except RuntimeError:\n            pass\n        self.assertTrue(self.db._state.closed)\n        self.assertTrue(self.db.is_closed())\n        async with self.db:\n            await self.create_record('after_error', 1)\n            self.assertFalse(self.db.is_closed())\n            await self.assertCount(1)\n\n        self.assertTrue(self.db.is_closed())\n\n    async def test_execute_sql(self):\n        iq, iparams = User.insert(username='x').sql()\n        sq, _= User.select().sql()\n        await self.db.aexecute_sql(iq, iparams)\n        r = await self.db.aexecute_sql(sq)\n        self.assertEqual(r.fetchall()[0][1], 'x')\n\n    async def test_multiple_tasks_raw_sql(self):\n        iq, _ = User.insert(username='x').sql()\n        sq, _ = User.select(User.username).where(User.username == 'x').sql()\n\n        async def worker(tid):\n            username = f'u{tid}'\n            await self.db.aconnect()\n            await self.db.aexecute_sql(iq, (username,))\n            r = await self.db.aexecute_sql(sq, (username,))\n            row = r.fetchone()\n            self.assertEqual(row[0], username)\n            await self.db.aclose()\n            return row\n\n        results = await asyncio.gather(*[worker(i) for i in range(3)])\n        self.assertEqual(sorted(results), [('u0',), ('u1',), ('u2',)])\n\n    async def test_list(self):\n        await self.seed(5)\n        query = TestModel.select().order_by(TestModel.value)\n        results = await self.db.list(query)\n        self.assertEqual(len(results), 5)\n        self.assertIsInstance(results[0], TestModel)\n        self.assertEqual([r.value for r in results], [0, 10, 20, 30, 40])\n\n    async def test_list_empty(self):\n        self.assertEqual(await self.db.list(TestModel.select()), [])\n\n    async def test_get(self):\n        rec = await self.create_record('unique', 999)\n        q = TestModel.select().where(TestModel.name == 'unique')\n        fetched = await self.db.get(q)\n        self.assertEqual(fetched.id, rec.id)\n        self.assertEqual(fetched.name, 'unique')\n        self.assertEqual(fetched.value, 999)\n\n    async def test_get_not_found(self):\n        with self.assertRaises(TestModel.DoesNotExist):\n            q = TestModel.select().where(TestModel.id == 0)\n            await self.db.get(q)\n\n    async def test_scalar(self):\n        await self.seed(10)\n        query = TestModel.select(fn.MAX(TestModel.value))\n        self.assertEqual(await self.db.scalar(query), 90)\n\n    async def test_scalar_no_results(self):\n        query = TestModel.select(fn.COUNT(TestModel.id))\n        self.assertEqual(await self.db.scalar(query), 0)\n\n        await self.seed(5)\n        self.assertEqual(await self.db.scalar(query), 5)\n\n    async def test_count(self):\n        self.assertEqual(await self.db.count(TestModel.select()), 0)\n        await self.seed(5)\n        self.assertEqual(await self.db.count(TestModel.select()), 5)\n\n    async def test_exists(self):\n        self.assertFalse(await self.db.exists(TestModel.select()))\n        await self.create_record('x', 1)\n        self.assertTrue(await self.db.exists(TestModel.select()))\n\n    async def test_aexecute(self):\n        q = TestModel.insert_many([(f'item{i}', i) for i in range(10)])\n        if self.support_returning:\n            q = q.returning(TestModel.name)\n            res = await self.db.aexecute(q)\n            self.assertEqual([t.name for t in res],\n                             [f'item{i}' for i in range(10)])\n        else:\n            await self.db.aexecute(q)\n        await self.assertCount(10)\n\n        q = (TestModel\n             .update(value=TestModel.value * 10)\n             .where(TestModel.value < 3))\n\n        if self.support_returning:\n            q = q.returning(TestModel.name, TestModel.value)\n            res = await self.db.aexecute(q)\n            self.assertEqual(sorted([(t.name, t.value) for t in res]),\n                             [('item0', 0), ('item1', 10), ('item2', 20)])\n        else:\n            res = await self.db.aexecute(q)\n            self.assertEqual(res, 2)\n\n        q = TestModel.select().where(TestModel.value >= 10)\n        self.assertEqual(await self.db.run(q.count), 2)\n\n        rows = await self.db.aexecute(q.order_by(TestModel.value))\n        self.assertEqual([r.name for r in rows], ['item1', 'item2'])\n\n        q = TestModel.delete().where(TestModel.value >= 10)\n        if self.support_returning:\n            q = q.returning(TestModel.name, TestModel.value)\n            res = await self.db.aexecute(q)\n            self.assertEqual(sorted([(t.name, t.value) for t in res]),\n                             [('item1', 10), ('item2', 20)])\n        else:\n            res = await self.db.aexecute(q)\n            self.assertEqual(res, 2)\n\n    async def test_run_contextvars(self):\n        var = contextvars.ContextVar('v', default='x')\n        state = []\n        def do_run():\n            state.append(var.get())\n        var.set('y')\n        state.append(var.get())\n        await self.db.run(do_run)\n        state.append(var.get())\n        self.assertEqual(state, ['y', 'y', 'y'])\n\n    async def test_create(self):\n        tm = await self.create_record('test1', 100)\n        self.assertEqual(tm.name, 'test1')\n        self.assertEqual(tm.value, 100)\n\n        tm = await self.db.run(TestModel.create, name='test2', value=101)\n        self.assertEqual(tm.name, 'test2')\n        self.assertEqual(tm.value, 101)\n\n        await self.assertCount(2)\n        await self.assertNames(['test1', 'test2'])\n\n    async def test_select(self):\n        tm = await self.create_record('test1', 100)\n        res = await self.db.list(TestModel.select())\n        self.assertEqual(len(res), 1)\n        self.assertEqual(res[0].name, 'test1')\n        self.assertEqual(res[0], tm)\n\n    async def test_filter(self):\n        await self.seed(20)\n        query = TestModel.select().where(TestModel.value > 100)\n        results = await self.db.list(query)\n        self.assertEqual(len(results), 9)\n\n    async def test_ordering(self):\n        await self.seed(20)\n        query = TestModel.select().order_by(TestModel.value.desc()).limit(5)\n        results = await self.db.list(query)\n        self.assertEqual(results[0].value, 190)\n        self.assertEqual(results[4].value, 150)\n\n    async def test_create_save_update(self):\n        await self.create_record('test1', 100)\n\n        def do_update():\n            r = TestModel.get(TestModel.name == 'test1')\n            r.value = 999; r.save()\n            return TestModel.get(TestModel.name == 'test1').value\n\n        self.assertEqual(await self.db.run(do_update), 999)\n\n        uq = TestModel.update(name='test1x').where(TestModel.name == 'test1')\n        res = await self.db.aexecute(uq)\n        #self.assertEqual(res, 1)\n\n        q = TestModel.select().where(TestModel.name == 'test1x')\n        tm = await self.db.get(q)\n        self.assertEqual(tm.value, 999)\n\n    async def test_update(self):\n        await self.seed(50)\n        await self.db.aexecute(TestModel\n                               .update(value=TestModel.value + 1000)\n                               .where(TestModel.value < 250))\n        query = TestModel.select().where(TestModel.value >= 1000)\n        self.assertEqual(await self.db.count(query), 25)\n\n        query = TestModel.select(fn.SUM(TestModel.value))\n        self.assertEqual(await self.db.scalar(query), 37250)\n\n    async def test_delete(self):\n        await self.seed(20)\n        await self.db.aexecute(TestModel.delete().where(TestModel.value < 50))\n        await self.assertCount(15)\n\n        tm = await self.db.get(TestModel.select())\n        await self.db.run(tm.delete_instance)\n        await self.assertCount(14)\n\n    async def test_bulk_create(self):\n        recs = [TestModel(name=f'b{i}', value=i) for i in range(100)]\n        await self.db.run(TestModel.bulk_create, recs, batch_size=25)\n        await self.assertCount(100)\n\n    async def test_bulk_update(self):\n        if self.driver == 'postgresql':\n            self.skipTest('bulk_update incompatible with asyncpg')\n            return\n\n        accum = [await self.db.run(TestModel.create, name=f'b{i}', value=i)\n                 for i in range(5)]\n        for tm in accum:\n            tm.name += '-x'\n            tm.value += 100\n\n        await self.db.run(TestModel.bulk_update, accum,\n                          fields=[TestModel.name, TestModel.value])\n\n        q = await self.db.list(TestModel.select().order_by(TestModel.value))\n        self.assertEqual([(tm.name, tm.value) for tm in q],\n                         [('b0-x', 100), ('b1-x', 101), ('b2-x', 102),\n                          ('b3-x', 103), ('b4-x', 104)])\n\n    async def test_insert_many(self):\n        def insert():\n            data = [{'name': f'i{i}', 'value': i} for i in range(100)]\n            TestModel.insert_many(data).execute()\n        await self.db.run(insert)\n        await self.assertCount(100)\n\n        data = [{'name': f'i{i}', 'value': i} for i in range(100, 200)]\n        await self.db.aexecute(TestModel.insert_many(data))\n        await self.assertCount(200)\n\n        data = [(f'i{i}', i) for i in range(200, 300)]\n        iq = (TestModel\n              .insert_many(data, fields=[TestModel.name, TestModel.value]))\n        await self.db.aexecute(iq)\n        await self.assertCount(300)\n\n    async def test_atomic(self):\n        async with self.db.atomic():\n            await self.create_record('a', 1)\n        await self.assertCount(1)\n\n        async with self.db.atomic() as txn:\n            await self.create_record('b', 2)\n            await self.assertCount(2)\n            await self.assertNames(['a', 'b'])\n            await txn.arollback()\n            await self.assertCount(1)\n            await self.create_record('c', 3)\n            await self.create_record('d', 4)\n\n        await self.assertCount(3)\n        await self.assertNames(['a', 'c', 'd'])\n\n    async def test_transaction_commit(self):\n        def create_in_tx():\n            with self.db.atomic():\n                TestModel.create(name='tx1')\n                TestModel.create(name='tx2')\n        await self.db.run(create_in_tx)\n        await self.assertCount(2)\n\n        async with self.db.atomic():\n            await self.db.run(TestModel.create, name='tx1')\n            await self.db.run(TestModel.create, name='tx2')\n        await self.assertCount(4)\n\n    async def test_transaction_rollback(self):\n        def failing():\n            with self.db.atomic():\n                TestModel.create(name='tx1')\n                raise ValueError('fail')\n        with self.assertRaises(ValueError):\n            await self.db.run(failing)\n        await self.assertCount(0)\n\n        async with self.db.atomic() as txn:\n            await self.create_record('tx2')\n            await self.assertCount(1)\n            await txn.arollback()\n        await self.assertCount(0)\n\n    async def test_nested_transactions(self):\n        def nested():\n            with self.db.atomic():\n                TestModel.create(name='o1', value=1)\n                with self.db.atomic():\n                    TestModel.create(name='i1', value=2)\n                    TestModel.create(name='i2', value=3)\n                TestModel.create(name='o2', value=4)\n        await self.db.run(nested)\n        await self.assertCount(4)\n        await self.assertNames(['i1', 'i2', 'o1', 'o2'])\n\n        async with self.db.atomic():\n            await self.db.run(TestModel.create, name='o3', value=1)\n            async with self.db.atomic():\n                await self.db.run(TestModel.create, name='i3', value=2)\n                await self.db.run(TestModel.create, name='i4', value=3)\n            await self.db.run(TestModel.create, name='o4', value=4)\n\n        await self.assertCount(8)\n        await self.assertNames(['i1', 'i2', 'i3', 'i4',\n                                'o1', 'o2', 'o3', 'o4'])\n\n    async def test_nested_implicit_rollback(self):\n        def nested():\n            with self.db.atomic():\n                TestModel.create(name='o1', value=1)\n                try:\n                    with self.db.atomic():\n                        TestModel.create(name='i1', value=2)\n                        raise ValueError('fail')\n                except ValueError:\n                    pass\n                TestModel.create(name='o2', value=3)\n        await self.db.run(nested)\n        await self.assertCount(2)\n        await self.assertNames(['o1', 'o2'])\n\n        async with self.db.atomic():\n            await self.db.run(TestModel.create, name='o3', value=1)\n            try:\n                async with self.db.atomic():\n                    await self.db.run(TestModel.create, name='i3', value=2)\n                    raise ValueError('fail')\n            except ValueError:\n                pass\n            await self.assertCount(3)\n            await self.db.run(TestModel.create, name='o4', value=3)\n\n        await self.assertCount(4)\n        await self.assertNames(['o1', 'o2', 'o3', 'o4'])\n\n    async def test_nested_explicit_rollback(self):\n        def nested():\n            with self.db.atomic():\n                TestModel.create(name='o1')\n                with self.db.atomic() as sp:\n                    TestModel.create(name='i1')\n                    self.assertEqual(TestModel.select().count(), 2)\n                    sp.rollback()\n                self.assertEqual(TestModel.select().count(), 1)\n                TestModel.create(name='o2')\n\n        await self.db.run(nested)\n        await self.assertCount(2)\n        await self.assertNames(['o1', 'o2'])\n\n        async with self.db.atomic():\n            await self.db.run(TestModel.create, name='o3')\n            async with self.db.atomic() as sp:\n                await self.db.run(TestModel.create, name='i2')\n                await self.assertCount(4)\n                await sp.arollback()\n            await self.assertCount(3)\n            await self.db.run(TestModel.create, name='o4')\n\n        await self.assertCount(4)\n        await self.assertNames(['o1', 'o2', 'o3', 'o4'])\n\n    async def test_nested_mix(self):\n        async with self.db.atomic():\n            await self.create_record('t1')\n            async with self.db.atomic():\n                await self.create_record('t2')\n                async with self.db.atomic():\n                    await self.create_record('t3')\n                try:\n                    async with self.db.atomic():\n                        await self.create_record('t4')\n                        await self.assertCount(4)\n                        raise ValueError('fail')\n                except ValueError:\n                    pass\n                async with self.db.atomic() as sp:\n                    await self.create_record('t4')\n                    await self.assertCount(4)\n                    await sp.arollback()\n                await self.assertCount(3)\n            try:\n                async with self.db.atomic():\n                    await self.create_record('t5')\n                    await self.assertCount(4)\n                    raise ValueError('fail')\n            except ValueError:\n                await self.assertCount(3)\n\n        await self.assertCount(3)\n        await self.assertNames(['t1', 't2', 't3'])\n\n        try:\n            async with self.db.atomic():\n                await self.create_record('t6')\n                async with self.db.atomic():\n                    await self.create_record('t7')\n                    async with self.db.atomic():\n                        await self.create_record('t8')\n                        await self.assertCount(6)\n                raise ValueError('fail')\n        except ValueError:\n            pass\n\n        await self.assertCount(3)\n        await self.assertNames(['t1', 't2', 't3'])\n\n    async def test_acommit_arollback(self):\n        async with self.db.atomic() as txn:\n            await self.create_record('committed', 1)\n            await txn.acommit()\n            await self.create_record('not-committed', 2)\n            await txn.arollback()\n\n        await self.assertCount(1)\n        await self.assertNames(['committed'])\n\n    async def test_concurrent_reads_writes(self):\n        await self.seed(10)\n\n        async def writer(sid):\n            def _write():\n                for i in range(5):\n                    TestModel.create(name=f'w{sid}-{i}', value=sid * 100 + i)\n\n            async with self.db:\n                await self.db.run(_write)\n\n        async def reader():\n            async with self.db:\n                query = TestModel.select()\n                return await self.db.run(lambda: len(list(query)))\n\n        await asyncio.gather(*[writer(i) for i in range(3)])\n        reads = await asyncio.gather(*[reader() for _ in range(3)])\n        self.assertTrue(all(r >= 10 for r in reads))\n        await self.assertCount(25)\n\n    async def test_isolated_connections_per_task(self):\n        async def worker(tid):\n            async with self.db:\n                c1 = self.db._state.conn\n                await self.create_record(f't{tid}', tid)\n                return c1 is self.db._state.conn\n\n        results = await asyncio.gather(*[worker(i) for i in range(5)])\n        self.assertTrue(all(results))\n        await self.assertCount(5)\n\n    async def test_many_concurrent_tasks(self):\n        ntasks = 50 if self.driver == 'sqlite' else 10\n        async def task(tid):\n            async with self.db:\n                await self.create_record(f't{tid}', tid)\n        await asyncio.gather(*[task(i) for i in range(ntasks)])\n        await self.assertCount(ntasks)\n\n    async def test_syntax_error_recovery(self):\n        with self.assertRaises(Exception):\n            await self.db.aexecute_sql('INVALID SQL')\n        await self.create_record('after_error', 1)\n        await self.assertCount(1)\n\n    async def test_concurrent_errors(self):\n        errors, successes = [], []\n\n        async def worker(tid):\n            async with self.db:\n                try:\n                    def work():\n                        TestModel.create(name=f't{tid}', value=tid)\n                        if tid % 2 == 0:\n                            raise ValueError(f'Task {tid} fails')\n                    await self.db.run(work)\n                    successes.append(tid)\n                except ValueError:\n                    errors.append(tid)\n\n        await asyncio.gather(*[worker(i) for i in range(10)])\n        self.assertEqual(sorted(errors), [0, 2, 4, 6, 8])\n        self.assertEqual(sorted(successes), [1, 3, 5, 7, 9])\n        await self.assertCount(10)\n\n    async def test_iterate_yields_model_instances(self):\n        await self.seed(20)\n        results = []\n        query = TestModel.select().order_by(TestModel.value)\n        async for obj in self.db.iterate(query):\n            results.append(obj)\n\n        self.assertEqual(len(results), 20)\n        self.assertTrue(all(isinstance(r, TestModel) for r in results))\n        self.assertEqual(results[0].name, 'item00')\n        self.assertEqual(results[0].value, 0)\n        self.assertEqual(results[-1].name, 'item19')\n        self.assertEqual(results[-1].value, 190)\n\n    async def test_iterate_matches_list(self):\n        await self.seed(20)\n        query = TestModel.select().order_by(TestModel.name)\n\n        eager = await self.db.list(query)\n        lazy = [obj async for obj in self.db.iterate(query)]\n        self.assertEqual(len(eager), len(lazy))\n\n        for e, l in zip(eager, lazy):\n            self.assertEqual(e.name, l.name)\n            self.assertEqual(e.value, l.value)\n\n    async def test_iterate_dicts(self):\n        await self.seed(5)\n        query = TestModel.select().order_by(TestModel.name)\n        results = [row async for row in self.db.iterate(query.dicts())]\n\n        self.assertEqual(len(results), 5)\n        self.assertIsInstance(results[0], dict)\n        self.assertEqual(results[0]['name'], 'item00')\n        self.assertEqual(results[-1]['name'], 'item04')\n\n    async def test_iterate_tuples(self):\n        await self.seed(5)\n        query = TestModel.select(TestModel.name).order_by(TestModel.name)\n        results = [row async for row in self.db.iterate(query.tuples())]\n\n        self.assertEqual(len(results), 5)\n        self.assertIsInstance(results[0], tuple)\n        self.assertEqual(results[0][0], 'item00')\n        self.assertEqual(results[-1][0], 'item04')\n\n    async def test_iterate_namedtuples(self):\n        await self.seed(5)\n        query = TestModel.select(TestModel.name).order_by(TestModel.name)\n        results = [row async for row in self.db.iterate(query.namedtuples())]\n\n        self.assertEqual(len(results), 5)\n        self.assertEqual(results[0].name, 'item00')\n        self.assertEqual(results[0][0], 'item00')\n        self.assertEqual(results[-1].name, 'item04')\n        self.assertEqual(results[-1][0], 'item04')\n\n    async def test_iterate_with_where(self):\n        await self.seed(20)\n        query = (TestModel.select()\n                 .where(TestModel.value >= 150)\n                 .order_by(TestModel.value))\n        results = [row async for row in self.db.iterate(query)]\n\n        self.assertEqual(len(results), 5)\n        self.assertEqual(results[0].value, 150)\n        self.assertEqual(results[-1].value, 190)\n\n    async def test_iterate_empty(self):\n        query = TestModel.select().where(TestModel.id == 0)\n        results = [row async for row in self.db.iterate(query)]\n        self.assertEqual(results, [])\n\n    async def test_iterate_buffer_size(self):\n        await self.seed(20)\n        query = TestModel.select().order_by(TestModel.value)\n        results = [obj async for obj in self.db.iterate(query, buffer_size=3)]\n\n        self.assertEqual(len(results), 20)\n        self.assertEqual(results[0].value, 0)\n        self.assertEqual(results[-1].value, 190)\n\n    async def test_iterate_early_break(self):\n        await self.seed(20)\n        count = 0\n        query = TestModel.select().order_by(TestModel.value)\n        async for obj in self.db.iterate(query):\n            count += 1\n            if count == 5:\n                break\n        self.assertEqual(count, 5)\n        # Database still usable (lock released).\n        self.assertEqual(await self.db.count(TestModel.select()), 20)\n\n    async def test_iterate_aggregation(self):\n        await self.seed(20)\n        query = (TestModel\n                 .select(fn.AVG(TestModel.value).alias('avg_val'))\n                 .dicts())\n        results = [row async for row in self.db.iterate(query)]\n\n        self.assertEqual(len(results), 1)\n        self.assertEqual(results[0]['avg_val'], 95.0)\n\n    async def test_iterate_sequential(self):\n        await self.seed(20)\n        query = (TestModel.select()\n                 .where(TestModel.value < 50)\n                 .order_by(TestModel.value))\n        r1 = [obj.value async for obj in self.db.iterate(query)]\n\n        query = (TestModel.select()\n                 .where(TestModel.value >= 150)\n                 .order_by(TestModel.value))\n        r2 = [obj.value async for obj in self.db.iterate(query)]\n\n        self.assertEqual(r1, [0, 10, 20, 30, 40])\n        self.assertEqual(r2, [150, 160, 170, 180, 190])\n\n    async def test_iterate_break_then_iterate_again(self):\n        await self.seed(20)\n        query = TestModel.select().order_by(TestModel.value)\n        async for obj in self.db.iterate(query):\n            break\n        results = []\n        async for obj in self.db.iterate(query):\n            results.append(obj.value)\n        self.assertEqual(len(results), 20)\n\n    async def test_iterate_multi(self):\n        await self.seed(10)\n        async def iterate_multi():\n            async with self.db:\n                query = TestModel.select().order_by(TestModel.value)\n                return [obj.id async for obj in self.db.iterate(query)]\n\n        results = await asyncio.gather(*[iterate_multi() for i in range(5)])\n        self.assertEqual(len(results), 5)\n        self.assertTrue(all(len(r) == 10 for r in results))\n\n    async def test_basic_crud(self):\n        rec = await self.create_record('testx', value=2)\n        self.assertEqual(rec.name, 'testx')\n        fetched = await self.db.run(TestModel.get, TestModel.name == 'testx')\n        self.assertEqual(fetched.value, 2)\n\n        def update():\n            r = TestModel.get(TestModel.id == rec.id)\n            r.value = 100; r.save()\n            return TestModel.get(TestModel.id == rec.id)\n        self.assertEqual((await self.db.run(update)).value, 100)\n\n        await self.db.run(rec.delete_instance)\n        await self.assertCount(0)\n\n    async def test_foreign_keys(self):\n        users = [User(username=f'u{i}') for i in range(3)]\n        await self.db.run(User.bulk_create, users)\n        self.assertEqual(await self.db.run(User.select().count), 3)\n        users = await self.db.list(User.select())\n\n        async with self.db.atomic():\n            for u in users:\n                for i in range(2):\n                    await self.db.run(\n                        Tweet.create, user=u, message=f'{u.username}-{i}')\n\n        self.assertEqual(await self.db.run(Tweet.select().count), 6)\n\n        q = Tweet.select().where(Tweet.message == 'u0-0')\n        tweet = await self.db.get(q)\n        self.assertEqual(await self.db.run(lambda: tweet.user.username), 'u0')\n\n        q = (Tweet.select(Tweet, User)\n             .join(User)\n             .where(Tweet.message == 'u0-0'))\n        tweet = await self.db.get(q)\n        self.assertEqual(tweet.user.username, 'u0')\n\n        q = User.select().where(User.username == 'u2')\n        user = await self.db.get(q)\n        tweets = await self.db.list(user.tweets.order_by(Tweet.id))\n        self.assertEqual([t.message for t in tweets], ['u2-0', 'u2-1'])\n\n        users_q = User.select().order_by(User.username)\n        tweets_q = Tweet.select().order_by(Tweet.message)\n        await self.db.aprefetch(users_q, tweets_q)\n        self.assertEqual(\n            [(u.username, [t.message for t in u.tweets]) for u in users_q],\n            [('u0', ['u0-0', 'u0-1']),\n             ('u1', ['u1-0', 'u1-1']),\n             ('u2', ['u2-0', 'u2-1'])])\n\n    async def test_transactions(self):\n        def ok_tx():\n            with self.db.atomic():\n                TestModel.create(name='t1', value=1)\n                TestModel.create(name='t2', value=2)\n        await self.db.run(ok_tx)\n        await self.assertCount(2)\n\n        def bad_tx():\n            with self.db.atomic():\n                TestModel.create(name='t3', value=3)\n                raise ValueError('fail')\n        with self.assertRaises(ValueError):\n            await self.db.run(bad_tx)\n\n        async with self.db.atomic():\n            await self.create_record('t4')\n            try:\n                async with self.db.atomic():\n                    await self.create_record('t5')\n                    await self.assertCount(4)\n                    raise ValueError('fail')\n            except ValueError:\n                pass\n            await self.assertCount(3)\n\n        await self.assertCount(3)\n        await self.assertNames(['t1', 't2', 't4'])\n\n\nclass TestSqliteIntegration(IntegrationTests, unittest.IsolatedAsyncioTestCase):\n    def get_database(self):\n        with tempfile.NamedTemporaryFile(delete=False) as f:\n            self.db_path = f.name\n        return AsyncSqliteDatabase(self.db_path)\n\n    async def test_pragmas(self):\n        db = AsyncSqliteDatabase(':memory:', pragmas={'user_version': '99'})\n        conn = await db.aconnect()\n        r = await conn.execute('PRAGMA user_version')\n        self.assertEqual(r.fetchone(), (99,))\n        await db.close_pool()\n\n    async def test_custom_functions(self):\n        db = AsyncSqliteDatabase(':memory:')\n\n        @db.func()\n        def title_case(s):\n            return s.title()\n\n        async with db:\n            r = await db.aexecute_sql('SELECT title_case(?)', ('test foo',))\n            self.assertEqual(r.fetchone(), ('Test Foo',))\n        await db.close_pool()\n\n    async def test_constraint_violation_recovery(self):\n        await self.db.aexecute_sql(\n            'CREATE TABLE ut (id INTEGER PRIMARY KEY, v TEXT UNIQUE)')\n        await self.db.aexecute_sql(\n            'INSERT INTO ut (v) VALUES (?)', ('x',))\n        with self.assertRaises(IntegrityError):\n            await self.db.aexecute_sql(\n                'INSERT INTO ut (v) VALUES (?)', ('x',))\n        await self.db.aexecute_sql(\n            'INSERT INTO ut (v) VALUES (?)', ('y',))\n\n\n@unittest.skipIf(not IS_POSTGRESQL, 'skipping postgres test')\n@unittest.skipUnless(asyncpg, 'asyncpg not installed')\nclass TestPostgresqlIntegration(IntegrationTests, unittest.IsolatedAsyncioTestCase):\n    def get_database(self):\n        return AsyncPostgresqlDatabase('peewee_test', **PSQL_PARAMS)\n\n    async def test_placeholder_conversion(self):\n        def insert():\n            return self.db.execute_sql(\n                'INSERT INTO testmodel (name, value) VALUES (%s, %s)',\n                ('placeholder_test', 999))\n        await self.db.run(insert)\n\n        def query():\n            r = self.db.execute_sql(\n                'SELECT * FROM testmodel WHERE name = %s',\n                ('placeholder_test',))\n            return r.fetchone()\n        row = await self.db.run(query)\n        self.assertIsNotNone(row)\n        self.assertEqual(row['name'], 'placeholder_test')\n        self.assertEqual(row['value'], 999)\n\n        curs = await self.db.aexecute_sql('select %s', ('test',))\n        self.assertEqual(curs.fetchone()[0], 'test')\n\n    async def test_iterator_with_transaction(self):\n        async with self.db.atomic() as tx:\n            await self.seed(2)\n            q = TestModel.select().order_by(TestModel.value)\n            results = [obj.value async for obj in self.db.iterate(q)]\n            self.assertEqual(results, [0, 10])\n\n        await self.assertCount(2)\n\n\n@unittest.skipIf(not IS_MYSQL, 'skipping mysql test')\n@unittest.skipUnless(aiomysql, 'aiomysql not installed')\nclass TestMySQLIntegration(IntegrationTests, unittest.IsolatedAsyncioTestCase):\n    def get_database(self):\n        return AsyncMySQLDatabase('peewee_test', **MYSQL_PARAMS)\n\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "tests/pwasyncio_stress.py",
    "content": "import asyncio\nimport os\nimport random\nimport sys\nimport time\nimport tracemalloc\nfrom playhouse.pwasyncio import *\n\n\ndef make_db_params(key):\n    params = {}\n    env_vars = [(part, 'PEEWEE_%s_%s' % (key, part.upper()))\n                for part in ('host', 'port', 'user', 'password')]\n    for param, env_var in env_vars:\n        value = os.environ.get(env_var)\n        if value:\n            params[param] = int(value) if param == 'port' else value\n    return params\n\nPSQL_PARAMS = make_db_params('PSQL')\n\n\nclass User(Model):\n    name = TextField()\n    email = TextField()\n\n    class Meta:\n        table_name = 'stress_test_users'\n\nasync def worker_task(db, task_id, num_operations=10):\n    User._meta.set_database(db)\n\n    try:\n        await db.aconnect()\n\n        for i in range(num_operations):\n            op = random.choice(['create', 'read', 'update', 'delete', 'transaction'])\n            name = 'User-%s-%s' % (task_id, i)\n            email = 'user%s_%s@test.com' % (task_id, i)\n\n            if op == 'create':\n                await db.run(User.create, name=name, email=email)\n\n            elif op == 'read':\n                users = await db.list(User.select().limit(5))\n\n            elif op == 'update':\n                users = await db.list(User.select().limit(1))\n                if users:\n                    user = users[0]\n                    user.name = 'Updated-%s-%s' % (task_id, i)\n                    await db.run(user.save)\n\n            elif op == 'delete':\n                users = await db.list(User.select().limit(1))\n                if users:\n                    await db.run(users[0].delete_instance)\n\n            elif op == 'transaction':\n                async with db.atomic():\n                    await db.run(User.create,\n                               name='TX-%s-%s' % (task_id, i),\n                               email='tx%s_%s@test.com' % (task_id, i))\n                    try:\n                        async with db.atomic():\n                            await db.run(User.create,\n                                         name='Nested-%s-%s' % (task_id, i),\n                                         email='nested%s_%s@test.com' % (task_id, i))\n                            if random.random() < 0.3:  # 30% chance of rollback\n                                raise ValueError('Intentional rollback')\n                    except ValueError:\n                        pass\n\n            # Small random delay to simulate real work\n            if random.random() < 0.1:\n                await asyncio.sleep(0.001)\n\n        # Close connection\n        await db.aclose()\n        return \"Task %s completed successfully\" % task_id\n\n    except Exception as exc:\n        print(exc)\n        return \"Task %s failed: %s\" % (task_id, exc)\n\n\nasync def stress_test(db, num_tasks=100, ops_per_task=10):\n    print('STRESS TEST: %s tasks x %s ops/task' % (num_tasks, ops_per_task))\n    print('Database: %s' % db)\n\n    User._meta.database = db\n\n    # Setup\n    print('Setting up database...')\n    async with db:\n        await db.acreate_tables([User])\n\n    # Track memory\n    tracemalloc.start()\n    initial_memory = tracemalloc.get_traced_memory()[0]\n\n    # Run stress test\n    print('Spawning %s concurrent tasks...' % num_tasks)\n    start_time = time.time()\n\n    tasks = [worker_task(db, i, ops_per_task) for i in range(num_tasks)]\n    results = await asyncio.gather(*tasks, return_exceptions=True)\n\n    elapsed = time.time() - start_time\n\n    # Check memory\n    final_memory = tracemalloc.get_traced_memory()[0]\n    memory_delta = (final_memory - initial_memory) / 1024 / 1024  # MB\n    tracemalloc.stop()\n\n    successful = sum(1 for r in results if isinstance(r, str) and 'completed' in r)\n    failed = len(results) - successful\n\n    # Check final state\n    async with db:\n        total_users = await db.run(User.select().count)\n\n    # Cleanup dead tasks\n    cleaned = db._state.cleanup_dead_tasks()\n\n    # Report\n    throughput = (num_tasks * ops_per_task) / elapsed\n    print('RESULTS')\n    print('Duration: %0.2fs' % elapsed)\n    print('Throughput: %0.1f ops/sec' % throughput)\n    print('Successful tasks: %s/%s' % (successful, num_tasks))\n    print('Failed tasks: %s/%s' % (failed, num_tasks))\n    print('Total users in DB: %s' % total_users)\n    print('Memory delta: %0.2f MB' % memory_delta)\n    print('Dead tasks cleaned: %s' % cleaned)\n    print('Remaining task states: %s' % len(db._state._state_storage))\n    print('-' * 60)\n\n    # Cleanup\n    async with db:\n        await db.adrop_tables([User])\n    await db.close_pool()\n\n    return successful == num_tasks\n\n\nasync def test_connection_isolation():\n    print('CONNECTION ISOLATION TEST')\n\n    db = AsyncPostgresqlDatabase('peewee_test', pool_size=5, **PSQL_PARAMS)\n    User._meta.database = db\n\n    async with db:\n        await db.acreate_tables([User])\n\n    async def task_with_transaction(task_id, delay):\n        \"\"\"Each task holds a transaction for a specific duration.\"\"\"\n        await db.aconnect()\n\n        async with db.atomic():\n            # Create a user\n            await db.run(User.create, name='Task-%s' % task_id, email='t%s@test.com' % task_id)\n\n            # Hold the transaction\n            await asyncio.sleep(delay)\n\n            # Verify we can still see our own changes\n            users = await db.run(list, User.select().where(User.name == 'Task-%s' % task_id))\n            assert len(users) == 1, 'Task %s lost its data!' % task_id\n\n        await db.aclose()\n        return 'Task %s isolated correctly' % task_id\n\n    # Spawn multiple tasks that will overlap in time\n    tasks = [\n        task_with_transaction(0, 0.1),\n        task_with_transaction(1, 0.2),\n        task_with_transaction(2, 0.15),\n        task_with_transaction(3, 0.05),\n    ]\n\n    results = await asyncio.gather(*tasks)\n    print('All tasks completed:')\n    for r in results:\n        print('  - %s' % r)\n\n    # Cleanup\n    async with db:\n        final_count = await db.run(User.select().count)\n        print('Final user count: %s (expected 4)' % final_count)\n        await db.adrop_tables([User])\n    await db.close_pool()\n\n    print('-' * 60)\n    return True\n\n\nasync def test_pool_exhaustion():\n    print('POOL EXHAUSTION TEST')\n\n    # Create a small pool\n    db = AsyncPostgresqlDatabase('peewee_test', pool_size=3, pool_min_size=1,\n                                 **PSQL_PARAMS)\n    User._meta.database = db\n\n    async with db:\n        await db.acreate_tables([User])\n\n    async def slow_task(task_id):\n        await db.run(db.connect)\n        await db.run(User.create, name='Slow-%s' % task_id, email='s%s@test.com' % task_id)\n        await asyncio.sleep(0.5)  # Hold connection\n        await db.run(db.close)\n        return task_id\n\n    print('Spawning 10 tasks with pool_size=3...')\n    print('(Tasks should queue and complete successfully)')\n\n    start = time.time()\n    tasks = [slow_task(i) for i in range(10)]\n    results = await asyncio.gather(*tasks)\n    elapsed = time.time() - start\n\n    print('All %s tasks completed in %.2fs' % (len(results), elapsed))\n    print('Expected ~1.5s (3 batches of parallel execution)')\n\n    # Cleanup\n    async with db:\n        await db.adrop_tables([User])\n    await db.close_pool()\n\n    print('-' * 60)\n    return True\n\n\nasync def main():\n    print('ASYNC PEEWEE STRESS TEST SUITE')\n    print('-' * 60)\n\n    # Test 1: Basic stress test with many tasks\n    db = AsyncPostgresqlDatabase('peewee_test', pool_size=20, **PSQL_PARAMS)\n    success1 = await stress_test(db, num_tasks=100, ops_per_task=20)\n\n    # Test 2: Even more tasks with smaller pool\n    db2 = AsyncPostgresqlDatabase('peewee_test', pool_size=5, **PSQL_PARAMS)\n    success2 = await stress_test(db2, num_tasks=200, ops_per_task=10)\n\n    # Test 3: Even smaller pool.\n    db3 = AsyncPostgresqlDatabase('peewee_test', pool_size=3, **PSQL_PARAMS)\n    success3 = await stress_test(db2, num_tasks=100, ops_per_task=20)\n\n    ## Test 3: Connection isolation\n    success4 = await test_connection_isolation()\n\n    ## Test 4: Pool exhaustion\n    success5 = await test_pool_exhaustion()\n\n    # Final report\n    print('=' * 60)\n    print('Stress Test 1 (100 tasks): %s' % 'OK' if success1 else 'FAIL')\n    print('Stress Test 2 (200 tasks): %s' % 'OK' if success2 else 'FAIL')\n    print('Stress Test 3 (100 tasks): %s' % 'OK' if success3 else 'FAIL')\n    print('Isolation Test: %s' % 'OK' if success4 else 'FAIL')\n    print('Pool Exhaustion Test: %s' % 'OK' if success5 else 'FAIL')\n    print('=' * 60)\n\n    if all([success1, success2, success3, success4, success5]):\n        print('Success')\n        return 0\n    else:\n        print('Failed')\n        return 1\n\n\nif __name__ == '__main__':\n    rc = asyncio.run(main())\n    sys.exit(rc)\n"
  },
  {
    "path": "tests/pwiz_integration.py",
    "content": "import datetime\nimport os\nimport textwrap\nimport sys\nfrom io import StringIO\nfrom unittest import mock\n\nfrom peewee import *\nfrom pwiz import *\n\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db_loader\nfrom .base import skip_if\n\n\ndb = db_loader('sqlite')\n\n\nclass User(TestModel):\n    username = CharField(primary_key=True)\n    id = IntegerField(default=0)\n\n\nclass Note(TestModel):\n    user = ForeignKeyField(User)\n    text = TextField(index=True)\n    data = IntegerField(default=0)\n    misc = IntegerField(default=0)\n\n    class Meta:\n        indexes = (\n            (('user', 'text'), True),\n            (('user', 'data', 'misc'), False),\n        )\n\n\nclass Category(TestModel):\n    name = CharField(unique=True)\n    parent = ForeignKeyField('self', null=True)\n\n\nclass OddColumnNames(TestModel):\n    spaces = CharField(column_name='s p aces')\n    symbols = CharField(column_name='w/-nug!')\n    camelCaseName = CharField(column_name='camelCaseName')\n    class Meta:\n        table_name = 'oddColumnNames'\n\n\nclass Event(TestModel):\n    data = TextField()\n    status = IntegerField()\n\n\nclass capture_output(object):\n    def __enter__(self):\n        self._stdout = sys.stdout\n        sys.stdout = self._buffer = StringIO()\n        return self\n\n    def __exit__(self, *args):\n        self.data = self._buffer.getvalue()\n        sys.stdout = self._stdout\n\n\nEXPECTED = \"\"\"\nfrom peewee import *\n\ndatabase = SqliteDatabase('peewee_test.db')\n\nclass UnknownField(object):\n    def __init__(self, *_, **__): pass\n\nclass BaseModel(Model):\n    class Meta:\n        database = database\n\nclass Category(BaseModel):\n    name = CharField(unique=True)\n    parent = ForeignKeyField(column_name='parent_id', field='id', model='self', null=True)\n\n    class Meta:\n        table_name = 'category'\n\nclass User(BaseModel):\n    id = IntegerField()\n    username = CharField(primary_key=True)\n\n    class Meta:\n        table_name = 'user'\n\nclass Note(BaseModel):\n    data = IntegerField()\n    misc = IntegerField()\n    text = TextField(index=True)\n    user = ForeignKeyField(column_name='user_id', field='username', model=User)\n\n    class Meta:\n        table_name = 'note'\n        indexes = (\n            (('user', 'data', 'misc'), False),\n            (('user', 'text'), True),\n        )\n\"\"\".strip()\n\nEXPECTED_ORDERED = \"\"\"\nfrom peewee import *\n\ndatabase = SqliteDatabase('peewee_test.db')\n\nclass UnknownField(object):\n    def __init__(self, *_, **__): pass\n\nclass BaseModel(Model):\n    class Meta:\n        database = database\n\nclass User(BaseModel):\n    username = CharField(primary_key=True)\n    id = IntegerField()\n\n    class Meta:\n        table_name = 'user'\n\nclass Note(BaseModel):\n    user = ForeignKeyField(column_name='user_id', field='username', model=User)\n    text = TextField(index=True)\n    data = IntegerField()\n    misc = IntegerField()\n\n    class Meta:\n        table_name = 'note'\n        indexes = (\n            (('user', 'data', 'misc'), False),\n            (('user', 'text'), True),\n        )\n\"\"\".strip()\n\n\nclass BasePwizTestCase(ModelTestCase):\n    database = db\n    requires = []\n\n    def setUp(self):\n        if not self.database.is_closed():\n            self.database.close()\n        if os.path.exists(self.database.database):\n            os.unlink(self.database.database)\n\n        super(BasePwizTestCase, self).setUp()\n        self.introspector = Introspector.from_database(self.database)\n\n\nclass TestPwiz(BasePwizTestCase):\n    requires = [User, Note, Category]\n\n    def test_print_models(self):\n        with capture_output() as output:\n            print_models(self.introspector)\n\n        self.assertEqual(output.data.strip(), EXPECTED)\n\n    def test_print_header(self):\n        cmdline = '-i -e sqlite %s' % db.database\n\n        with capture_output() as output:\n            with mock.patch('pwiz.datetime.datetime') as mock_datetime:\n                now = mock_datetime.now.return_value\n                now.strftime.return_value = 'February 03, 2015 15:30PM'\n                print_header(cmdline, self.introspector)\n\n        self.assertEqual(output.data.strip(), (\n            '# Code generated by:\\n'\n            '# python -m pwiz %s\\n'\n            '# Date: February 03, 2015 15:30PM\\n'\n            '# Database: %s\\n'\n            '# Peewee version: %s') % (cmdline, db.database, peewee_version))\n\n\nclass TestPwizOrdered(BasePwizTestCase):\n    requires = [User, Note]\n\n    def test_ordered_columns(self):\n        with capture_output() as output:\n            print_models(self.introspector, preserve_order=True)\n\n        self.assertEqual(output.data.strip(), EXPECTED_ORDERED)\n\n\nclass TestPwizUnknownField(BasePwizTestCase):\n    header = ('from peewee import *\\n\\n'\n              'database = SqliteDatabase(\\'peewee_test.db\\')\\n\\n')\n    unknown = ('class UnknownField(object):\\n'\n               '    def __init__(self, *_, **__): pass\\n\\n')\n    basemodel = ('class BaseModel(Model):\\n    class Meta:\\n'\n                 '        database = database\\n\\n')\n\n    def setUp(self):\n        super(TestPwizUnknownField, self).setUp()\n        self.database.execute_sql(\n            'CREATE TABLE \"foo\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"unk1\", \"unk2\" BIZBAZ NOT NULL)')\n\n    def test_unknown_field(self):\n        with capture_output() as output:\n            print_models(self.introspector)\n\n        self.assertEqual(output.data.strip(), (\n            self.header + self.unknown + self.basemodel +\n            'class Foo(BaseModel):\\n'\n            '    unk1 = BareField(null=True)\\n'\n            '    unk2 = UnknownField()  # BIZBAZ\\n\\n'\n            '    class Meta:\\n        table_name = \\'foo\\''))\n\n    def test_ignore_unknown(self):\n        with capture_output() as output:\n            print_models(self.introspector, ignore_unknown=True)\n\n        self.assertEqual(output.data.strip(), (\n            self.header + self.basemodel +\n            'class Foo(BaseModel):\\n'\n            '    unk1 = BareField(null=True)\\n'\n            '    # unk2 - BIZBAZ\\n\\n'\n            '    class Meta:\\n        table_name = \\'foo\\''))\n\n\nclass TestPwizInvalidColumns(BasePwizTestCase):\n    requires = [OddColumnNames]\n\n    def test_invalid_columns(self):\n        with capture_output() as output:\n            print_models(self.introspector)\n\n        result = output.data.strip()\n        expected = textwrap.dedent(\"\"\"\n            class OddColumnNames(BaseModel):\n                camel_case_name = CharField(column_name='camelCaseName')\n                s_p_aces = CharField(column_name='s p aces')\n                w_nug_ = CharField(column_name='w/-nug!')\n\n                class Meta:\n                    table_name = 'oddColumnNames'\"\"\").strip()\n\n        actual = result[-len(expected):]\n        self.assertEqual(actual, expected)\n\n    def test_odd_columns_legacy(self):\n        with capture_output() as output:\n            print_models(self.introspector, snake_case=False)\n\n        result = output.data.strip()\n        expected = textwrap.dedent(\"\"\"\n            class Oddcolumnnames(BaseModel):\n                camelcasename = CharField(column_name='camelCaseName')\n                s_p_aces = CharField(column_name='s p aces')\n                w_nug_ = CharField(column_name='w/-nug!')\n\n                class Meta:\n                    table_name = 'oddColumnNames'\"\"\").strip()\n\n        actual = result[-len(expected):]\n        self.assertEqual(actual, expected)\n\n\nclass TestPwizIntrospectViews(BasePwizTestCase):\n    requires = [Event]\n\n    def setUp(self):\n        super(TestPwizIntrospectViews, self).setUp()\n        self.database.execute_sql('CREATE VIEW \"events_public\" AS '\n                                  'SELECT data FROM event WHERE status = 1')\n\n    def tearDown(self):\n        self.database.execute_sql('DROP VIEW \"events_public\"')\n        super(TestPwizIntrospectViews, self).tearDown()\n\n    def test_introspect_ignore_views(self):\n        # By default views are not included in the output.\n        with capture_output() as output:\n            print_models(self.introspector)\n        self.assertFalse('events_public' in output.data.strip())\n\n    def test_introspect_views(self):\n        # Views can be introspected, however.\n        with capture_output() as output:\n            print_models(self.introspector, include_views=True)\n\n        result = output.data.strip()\n        event_tbl = textwrap.dedent(\"\"\"\n            class Event(BaseModel):\n                data = TextField()\n                status = IntegerField()\n\n                class Meta:\n                    table_name = 'event'\"\"\").strip()\n        self.assertTrue(event_tbl in result)\n\n        event_view = textwrap.dedent(\"\"\"\n            class EventsPublic(BaseModel):\n                data = TextField(null=True)\n\n                class Meta:\n                    table_name = 'events_public'\n                    primary_key = False\"\"\").strip()\n        self.assertTrue(event_view in result)\n"
  },
  {
    "path": "tests/pydantic_utils.py",
    "content": "from __future__ import annotations\n\nimport datetime\nimport decimal\nimport uuid\nfrom typing import List\n\nfrom peewee import *\nfrom playhouse.pydantic_utils import to_pydantic\nfrom pydantic import BaseModel\n\nfrom .base import ModelDatabaseTestCase\nfrom .base import get_in_memory_db\nfrom .base import requires_models\nfrom .base import TestModel\n\n\nclass User(TestModel):\n    name = CharField(verbose_name='Full Name', help_text='Display name')\n    age = IntegerField()\n    active = BooleanField(default=True)\n    bio = TextField(null=True)\n    score = FloatField(null=True, default=0.0)\n    status = CharField(\n        verbose_name='Status',\n        help_text='Record status',\n        choices=[\n            ('active', 'Active'),\n            ('archived', 'Archived'),\n            ('deleted', 'Deleted')])\n    created = DateTimeField(default=datetime.datetime.now)\n\n\nclass Tweet(TestModel):\n    user = ForeignKeyField(User, backref='tweets')\n    content = TextField()\n    created = DateTimeField(default=datetime.datetime.now)\n\n\nclass NullableFK(TestModel):\n    user = ForeignKeyField(User, null=True, backref='nullable_things')\n    label = CharField()\n\n\nclass AllTypes(TestModel):\n    f_text = TextField()\n    f_blob = BlobField()\n    f_bool = BooleanField()\n    f_date = DateField()\n    f_datetime = DateTimeField()\n    f_decimal = DecimalField()\n    f_double = DoubleField()\n    f_float = FloatField()\n    f_int = IntegerField()\n    f_smallint = SmallIntegerField()\n    f_time = TimeField()\n    f_uuid = UUIDField()\n    f_char = CharField()\n\n\nclass BasePydanticTestCase(ModelDatabaseTestCase):\n    database = get_in_memory_db()\n\n\nclass TestPydanticConversion(BasePydanticTestCase):\n    def test_conversion(self):\n        Schema = to_pydantic(User)\n        self.assertTrue(issubclass(Schema, BaseModel))\n        self.assertEqual(Schema.__name__, 'UserSchema')\n        self.assertEqual(set(Schema.model_fields), {\n            'name', 'age', 'active', 'bio', 'score', 'status', 'created'})\n\n    def test_application(self):\n        Schema = to_pydantic(User)\n        obj = Schema(name='Huey', age=14, status='active')\n        ts = obj.created\n        self.assertEqual(obj.dict(), {\n            'name': 'Huey',\n            'age': 14,\n            'active': True,\n            'bio': None,\n            'score': 0.0,\n            'status': 'active',\n            'created': ts})\n\n        with self.assertRaises(ValueError) as ctx:\n            obj = Schema()\n\n        self.assertTrue('3 validation errors' in str(ctx.exception))\n\n        with self.assertRaises(ValueError) as ctx:\n            obj = Schema(name='Huey', age=14)\n\n        self.assertTrue('1 validation error' in str(ctx.exception))\n\n        with self.assertRaises(ValueError) as ctx:\n            obj = Schema(name='Huey', age=14, status='invalid')\n\n        self.assertTrue('Input should be' in str(ctx.exception))\n\n    def test_autofield(self):\n        Schema = to_pydantic(User)\n        self.assertNotIn('id', Schema.model_fields)\n\n        Schema = to_pydantic(User, exclude_autofield=False)\n        self.assertIn('id', Schema.model_fields)\n\n    def test_nullable(self):\n        Schema = to_pydantic(User)\n        self.assertTrue(Schema.model_fields['name'].is_required())\n        self.assertTrue(Schema.model_fields['age'].is_required())\n        self.assertFalse(Schema.model_fields['bio'].is_required())\n        self.assertFalse(Schema.model_fields['score'].is_required())\n\n        self.assertIsNone(Schema.model_fields['bio'].default)\n        self.assertEqual(Schema.model_fields['score'].default, 0.0)\n\n    def test_defaults(self):\n        Schema = to_pydantic(User)\n        obj = Schema(name='Huey', age=14, status='active')\n        self.assertTrue(obj.active)\n        self.assertEqual(obj.score, 0.0)\n        self.assertIsNone(obj.bio)\n        self.assertTrue(isinstance(obj.created, datetime.datetime))\n\n    def test_choices(self):\n        Schema = to_pydantic(User, include='status')\n\n        for choice in ('active', 'archived', 'deleted'):\n            instance = Schema(status=choice)\n            self.assertEqual(instance.status, choice)\n\n        with self.assertRaises(ValueError):\n            instance = Schema(status='invalid')\n\n    def test_metadata(self):\n        Schema = to_pydantic(User)\n        self.assertEqual(Schema.model_fields['name'].title, 'Full Name')\n        self.assertEqual(Schema.model_fields['status'].title, 'Status')\n        self.assertIsNone(Schema.model_fields['age'].title)\n\n        self.assertIn('Display name', Schema.model_fields['name'].description)\n        self.assertIsNone(Schema.model_fields['age'].description)\n\n        desc = Schema.model_fields['status'].description\n        self.assertIn('Record status', desc)\n        self.assertIn(\"'active' = Active\", desc)\n        self.assertIn(\"'deleted' = Deleted\", desc)\n\n        jschema = Schema.model_json_schema()\n        self.assertEqual(jschema['properties']['name']['title'], 'Full Name')\n\n    def test_foreign_key(self):\n        Schema = to_pydantic(Tweet)\n        self.assertEqual(set(Schema.model_fields),\n                         {'user_id', 'content', 'created'})\n\n        obj = Schema(user_id=1337, content='Test')\n        self.assertEqual(obj.user_id, 1337)\n        self.assertEqual(obj.content, 'Test')\n\n        with self.assertRaises(ValueError):\n            Schema(user_id='not_an_int', content='test')\n\n    def test_type_mapping(self):\n        Schema = to_pydantic(AllTypes)\n        valid_data = {\n            'f_blob': b'\\x00\\x01',\n            'f_bool': True,\n            'f_char': 'world',\n            'f_date': datetime.date.today(),\n            'f_datetime': datetime.datetime.now(),\n            'f_decimal': decimal.Decimal('3.14'),\n            'f_double': 2.718,\n            'f_float': 1.5,\n            'f_int': 42,\n            'f_smallint': 7,\n            'f_text': 'hello',\n            'f_time': datetime.time(12, 14),\n            'f_uuid': uuid.uuid4(),\n        }\n        instance = Schema(**valid_data)\n        for key, val in valid_data.items():\n            self.assertEqual(getattr(instance, key), val)\n\n    def test_include_exclude(self):\n        Schema = to_pydantic(User, exclude={'age', 'bio'})\n        self.assertNotIn('age', Schema.model_fields)\n        self.assertNotIn('bio', Schema.model_fields)\n        self.assertIn('name', Schema.model_fields)\n\n        Schema = to_pydantic(User, include={'name', 'status'})\n        self.assertEqual(set(Schema.model_fields), {'name', 'status'})\n\n        Schema = to_pydantic(User, include={'name', 'age'}, exclude={'age'})\n        self.assertEqual(set(Schema.model_fields), {'name'})\n\n    def test_nullable_fields(self):\n        Schema = to_pydantic(User)\n        self.assertTrue(Schema.model_fields['name'].is_required())\n        self.assertTrue(Schema.model_fields['age'].is_required())\n\n        self.assertEqual(Schema.model_fields['bio'].default, None)\n        self.assertFalse(Schema.model_fields['bio'].is_required())\n\n        instance = Schema(name='a', age=1, status='active')\n        self.assertEqual(instance.score, 0.0)\n        self.assertIsNone(instance.bio)\n        self.assertEqual(instance.active, True)\n        self.assertIsInstance(instance.created, datetime.datetime)\n\n    def test_schema_generation(self):\n        for model in (User, Tweet, AllTypes):\n            with self.subTest(model=model.__name__):\n                Schema = to_pydantic(model)\n                schema = Schema.model_json_schema()\n                self.assertIn('properties', schema)\n\n        Schema = to_pydantic(User)\n        schema = Schema.model_json_schema()\n        bio_schema = schema['properties']['bio']\n        any_of_types = [s.get('type') for s in bio_schema.get('anyOf', [])]\n        self.assertIn('null', any_of_types)\n\n    @requires_models(User)\n    def test_validate_model(self):\n        Schema = to_pydantic(User)\n\n        u = User.create(name='Huey', age=14, status='active')\n        validated = Schema.model_validate(u)\n        self.assertEqual(validated.dict(), {\n            'name': 'Huey',\n            'age': 14,\n            'active': True,\n            'bio': None,\n            'score': 0.0,\n            'status': 'active',\n            'created': u.created})\n\n        us = User(**validated.dict())\n        self.assertEqual(us.name, 'Huey')\n        self.assertEqual(us.age, 14)\n        self.assertTrue(us.active)\n        self.assertIsNone(us.bio)\n        self.assertEqual(us.score, 0.0)\n        self.assertEqual(us.status, 'active')\n        self.assertEqual(us.created, u.created)\n        self.assertIsNone(us.id)\n\n        v2 = Schema.model_validate(validated.dict())\n        self.assertEqual(validated, v2)\n\n    @requires_models(User, Tweet)\n    def test_validate_model_foreign_key(self):\n        Schema = to_pydantic(Tweet)\n\n        user = User.create(name='Huey', age=14, status='active')\n        tweet = Tweet.create(user=user, content='hello')\n\n        validated = Schema.model_validate(tweet)\n        self.assertEqual(validated.dict(), {\n            'content': 'hello',\n            'created': tweet.created,\n            'user_id': user.id})\n\n        ts = Tweet(**validated.dict())\n        self.assertEqual(ts.content, 'hello')\n        self.assertEqual(ts.user_id, user.id)\n        self.assertEqual(ts.user.name, 'Huey')  # Triggers query.\n        self.assertIsNone(ts.id)\n\n        v2 = Schema.model_validate(validated.dict())\n        self.assertEqual(validated, v2)\n\n\nclass TestRelationships(BasePydanticTestCase):\n    def test_nested_schema(self):\n        UserSchema = to_pydantic(User, exclude_autofield=False)\n        TweetResponse = to_pydantic(\n            Tweet,\n            exclude_autofield=False,\n            relationships={Tweet.user: UserSchema})\n\n        self.assertEqual(set(TweetResponse.model_fields),\n                         {'id', 'user', 'content', 'created'})\n\n        instance = TweetResponse(\n            id=1,\n            user={'id': 1, 'name': 'Huey', 'age': 14, 'status': 'active'},\n            content='hello')\n        self.assertEqual(instance.user.name, 'Huey')\n        self.assertEqual(instance.content, 'hello')\n\n        with self.assertRaises(ValueError):\n            TweetResponse(id=1, user=42, content='hello')\n\n        OtherSchema = to_pydantic(Tweet, relationships={})\n        self.assertIn('user_id', OtherSchema.model_fields)\n\n    def test_nested_relationship_in_json_schema(self):\n        UserSchema = to_pydantic(User, exclude_autofield=False)\n        TweetResponse = to_pydantic(\n            Tweet, exclude_autofield=False,\n            relationships={Tweet.user: UserSchema})\n\n        schema = TweetResponse.model_json_schema()\n        self.assertIn('user', schema['properties'])\n\n    def test_nullable_fk_relationship(self):\n        UserSchema = to_pydantic(User, exclude_autofield=False)\n        Schema = to_pydantic(\n            NullableFK,\n            exclude_autofield=False,\n            relationships={NullableFK.user: UserSchema})\n\n        instance = Schema(id=1, user=None, label='test')\n        self.assertIsNone(instance.user)\n\n        instance = Schema(id=1, label='test', user={\n            'id': 1,\n            'name': 'Huey',\n            'age': 14,\n            'status': 'active'})\n        self.assertEqual(instance.user.name, 'Huey')\n\n    def test_metadata_preserved_on_nested_field(self):\n        UserSchema = to_pydantic(User, exclude_autofield=False)\n        Schema = to_pydantic(\n            NullableFK, exclude_autofield=False,\n            relationships={NullableFK.user: UserSchema})\n\n        field_info = Schema.model_fields['user']\n        self.assertFalse(field_info.is_required())\n\n    def test_backref(self):\n        TweetFlat = to_pydantic(Tweet, exclude_autofield=False)\n        UserDetail = to_pydantic(\n            User, exclude_autofield=False,\n            relationships={User.tweets: List[TweetFlat]})\n\n        self.assertEqual(set(UserDetail.model_fields), {\n            'id', 'name', 'age', 'active', 'bio', 'score', 'status',\n            'created', 'tweets'})\n\n        instance = UserDetail(id=1, name='Huey', age=14, status='active')\n        self.assertEqual(instance.tweets, [])\n\n        instance = UserDetail(\n            id=1, name='Huey', age=14, status='active',\n            tweets=[\n                {'id': 1, 'user_id': 1, 'content': 'hello'},\n                {'id': 2, 'user_id': 1, 'content': 'world'},\n            ])\n        self.assertEqual(len(instance.tweets), 2)\n        self.assertEqual(instance.tweets[0].content, 'hello')\n\n        with self.assertRaises(ValueError):\n            UserDetail(id=1, name='Huey', age=14, status='active',\n                       tweets=[{'bad': 'data'}])\n\n    @requires_models(User, Tweet)\n    def test_validate_fk(self):\n        UserSchema = to_pydantic(User, exclude_autofield=False)\n        TweetResponse = to_pydantic(\n            Tweet, exclude_autofield=False,\n            relationships={Tweet.user: UserSchema})\n\n        user = User.create(name='Huey', age=14, status='active')\n        Tweet.create(user=user, content='hello')\n\n        # Re-fetch so rel is not populated.\n        tweet = Tweet.select().get()\n\n        with self.assertQueryCount(1):\n            result = TweetResponse.model_validate(tweet)\n            self.assertEqual(result.content, 'hello')\n            self.assertEqual(result.user.name, 'Huey')\n            self.assertEqual(result.user.age, 14)\n\n        tweet = (Tweet\n                 .select(Tweet, User)\n                 .join(User)\n                 .get())\n\n        with self.assertQueryCount(0):\n            result = TweetResponse.model_validate(tweet)\n            self.assertEqual(result.content, 'hello')\n            self.assertEqual(result.user.name, 'Huey')\n            self.assertEqual(result.user.age, 14)\n\n    @requires_models(User, Tweet)\n    def test_validate_backref(self):\n        TweetFlat = to_pydantic(Tweet, exclude_autofield=False,\n                                exclude={'user'})\n        UserDetail = to_pydantic(\n            User, exclude_autofield=False,\n            relationships={User.tweets: List[TweetFlat]})\n\n        user = User.create(name='Huey', age=14, status='active')\n        Tweet.create(user=user, content=f't0')\n        Tweet.create(user=user, content=f't1')\n\n        # Will evaluate user.tweets on demand.\n        with self.assertQueryCount(1):\n            result = UserDetail.model_validate(user)\n            self.assertEqual(result.name, 'Huey')\n            self.assertEqual(sorted([t.content for t in result.tweets]),\n                             ['t0', 't1'])\n\n        # Will use prefetched tweets.\n        user = User.select().prefetch(Tweet.select().order_by(Tweet.id))[0]\n        with self.assertQueryCount(0):\n            result = UserDetail.model_validate(user)\n            self.assertEqual(result.name, 'Huey')\n            self.assertEqual([t.content for t in result.tweets], ['t0', 't1'])\n"
  },
  {
    "path": "tests/queries.py",
    "content": "from peewee import *\n\nfrom .base import BaseTestCase\nfrom .base import DatabaseTestCase\nfrom .base import TestModel\nfrom .base import get_in_memory_db\n\n\nUser = Table('users', ['id', 'username'])\nTweet = Table('tweet', ['id', 'user_id', 'content'])\nRegister = Table('register', ['id', 'value'])\n\n\nclass TestQueryExecution(DatabaseTestCase):\n    database = get_in_memory_db()\n\n    def setUp(self):\n        super(TestQueryExecution, self).setUp()\n        User.bind(self.database)\n        Tweet.bind(self.database)\n        Register.bind(self.database)\n        self.execute('CREATE TABLE \"users\" (id INTEGER NOT NULL PRIMARY KEY, '\n                     'username TEXT)')\n        self.execute('CREATE TABLE \"tweet\" (id INTEGER NOT NULL PRIMARY KEY, '\n                     'user_id INTEGER NOT NULL, content TEXT, FOREIGN KEY '\n                     '(user_id) REFERENCES users (id))')\n        self.execute('CREATE TABLE \"register\" ('\n                     'id INTEGER NOT NULL PRIMARY KEY, '\n                     'value REAL)')\n\n    def tearDown(self):\n        self.execute('DROP TABLE \"tweet\";')\n        self.execute('DROP TABLE \"users\";')\n        self.execute('DROP TABLE \"register\";')\n        super(TestQueryExecution, self).tearDown()\n\n    def create_user_tweets(self, username, *tweets):\n        user_id = User.insert({User.username: username}).execute()\n        for tweet in tweets:\n            Tweet.insert({\n                Tweet.user_id: user_id,\n                Tweet.content: tweet}).execute()\n        return user_id\n\n    def test_selection(self):\n        huey_id = self.create_user_tweets('huey', 'meow', 'purr')\n        query = User.select()\n        self.assertEqual(query[:], [{'id': huey_id, 'username': 'huey'}])\n\n        query = (Tweet\n                 .select(Tweet.content, User.username)\n                 .join(User, on=(Tweet.user_id == User.id))\n                 .order_by(Tweet.id))\n        self.assertEqual(query[:], [\n            {'content': 'meow', 'username': 'huey'},\n            {'content': 'purr', 'username': 'huey'}])\n\n    def test_select_peek_first(self):\n        huey_id = self.create_user_tweets('huey', 'meow', 'purr', 'hiss')\n        query = Tweet.select(Tweet.content).order_by(Tweet.id)\n        self.assertEqual(query.peek(n=2), [\n            {'content': 'meow'},\n            {'content': 'purr'}])\n        self.assertEqual(query.first(), {'content': 'meow'})\n\n        query = Tweet.select().where(Tweet.id == 0)\n        self.assertIsNone(query.peek(n=2))\n        self.assertIsNone(query.first())\n\n    def test_select_get(self):\n        huey_id = self.create_user_tweets('huey')\n        self.assertEqual(User.select().where(User.username == 'huey').get(), {\n            'id': huey_id, 'username': 'huey'})\n        self.assertIsNone(User.select().where(User.username == 'x').get())\n\n    def test_select_count(self):\n        huey_id = self.create_user_tweets('huey', 'meow', 'purr')\n        mickey_id = self.create_user_tweets('mickey', 'woof', 'pant', 'whine')\n\n        self.assertEqual(User.select().count(), 2)\n        self.assertEqual(Tweet.select().count(), 5)\n\n        query = Tweet.select().where(Tweet.user_id == mickey_id)\n        self.assertEqual(query.count(), 3)\n\n        query = (Tweet\n                 .select()\n                 .join(User, on=(Tweet.user_id == User.id))\n                 .where(User.username == 'foo'))\n        self.assertEqual(query.count(), 0)\n\n    def test_select_exists(self):\n        self.create_user_tweets('huey')\n        self.assertTrue(User.select().where(User.username == 'huey').exists())\n        self.assertFalse(User.select().where(User.username == 'foo').exists())\n\n    def test_scalar(self):\n        values = [1.0, 1.5, 2.0, 5.0, 8.0]\n        (Register\n         .insert([{Register.value: value} for value in values])\n         .execute())\n\n        query = Register.select(fn.AVG(Register.value))\n        self.assertEqual(query.scalar(), 3.5)\n\n        query = query.where(Register.value < 5)\n        self.assertEqual(query.scalar(), 1.5)\n\n        query = (Register\n                 .select(\n                     fn.SUM(Register.value),\n                     fn.COUNT(Register.value),\n                     fn.SUM(Register.value) / fn.COUNT(Register.value)))\n        self.assertEqual(query.scalar(as_tuple=True), (17.5, 5, 3.5))\n\n        query = query.where(Register.value >= 2)\n        self.assertEqual(query.scalar(as_tuple=True), (15, 3, 5))\n\n    def test_scalars(self):\n        values = [1.0, 1.5, 2.0, 5.0, 8.0]\n        (Register\n         .insert([{Register.value: value} for value in values])\n         .execute())\n\n        query = Register.select(Register.value).order_by(Register.value)\n        self.assertEqual(list(query.scalars()), values)\n\n        query = query.where(Register.value < 5)\n        self.assertEqual(list(query.scalars()), [1.0, 1.5, 2.0])\n\n    def test_slicing_select(self):\n        values = [1., 1., 2., 3., 5., 8.]\n        (Register\n         .insert([(v,) for v in values], columns=(Register.value,))\n         .execute())\n\n        query = (Register\n                 .select(Register.value)\n                 .order_by(Register.value)\n                 .tuples())\n        with self.assertQueryCount(1):\n            self.assertEqual(query[0], (1.,))\n            self.assertEqual(query[:2], [(1.,), (1.,)])\n            self.assertEqual(query[1:4], [(1.,), (2.,), (3.,)])\n            self.assertEqual(query[-1], (8.,))\n            self.assertEqual(query[-2], (5.,))\n            self.assertEqual(query[-2:], [(5.,), (8.,)])\n            self.assertEqual(query[2:-2], [(2.,), (3.,)])\n\n\nclass TestQueryCloning(BaseTestCase):\n    def test_clone_tables(self):\n        self._do_test_clone(User, Tweet)\n\n    def test_clone_models(self):\n        class User(TestModel):\n            username = TextField()\n            class Meta:\n                table_name = 'users'\n        class Tweet(TestModel):\n            user = ForeignKeyField(User, backref='tweets')\n            content = TextField()\n        self._do_test_clone(User, Tweet)\n\n    def _do_test_clone(self, User, Tweet):\n        query = Tweet.select(Tweet.id)\n        base_sql = 'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\"'\n        self.assertSQL(query, base_sql, [])\n\n        qj = query.join(User, on=(Tweet.user_id == User.id))\n        self.assertSQL(query, base_sql, [])\n        self.assertSQL(qj, (\n            'SELECT \"t1\".\"id\" FROM \"tweet\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\")'), [])\n\n        qw = query.where(Tweet.id > 3)\n        self.assertSQL(query, base_sql, [])\n        self.assertSQL(qw, base_sql + ' WHERE (\"t1\".\"id\" > ?)', [3])\n\n        qw2 = qw.where(Tweet.id < 6)\n        self.assertSQL(query, base_sql, [])\n        self.assertSQL(qw, base_sql + ' WHERE (\"t1\".\"id\" > ?)', [3])\n        self.assertSQL(qw2, base_sql + (' WHERE ((\"t1\".\"id\" > ?) '\n                                        'AND (\"t1\".\"id\" < ?))'), [3, 6])\n\n        qo = query.order_by(Tweet.id)\n        self.assertSQL(query, base_sql, [])\n        self.assertSQL(qo, base_sql + ' ORDER BY \"t1\".\"id\"', [])\n\n        qo2 = qo.order_by(Tweet.content, Tweet.id)\n        self.assertSQL(query, base_sql, [])\n        self.assertSQL(qo, base_sql + ' ORDER BY \"t1\".\"id\"', [])\n        self.assertSQL(qo2,\n                       base_sql + ' ORDER BY \"t1\".\"content\", \"t1\".\"id\"', [])\n\n        qg = query.group_by(Tweet.id)\n        self.assertSQL(query, base_sql, [])\n        self.assertSQL(qg, base_sql + ' GROUP BY \"t1\".\"id\"', [])\n"
  },
  {
    "path": "tests/reflection.py",
    "content": "import datetime\nimport os\nimport re\nimport warnings\n\nfrom peewee import *\nfrom playhouse.reflection import *\n\nfrom .base import IS_CRDB\nfrom .base import IS_CYSQLITE\nfrom .base import IS_SQLITE_OLD\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db\nfrom .base import requires_models\nfrom .base import requires_sqlite\nfrom .base import skip_if\nfrom .base_models import Tweet\nfrom .base_models import User\n\n\nclass ColTypes(TestModel):\n    f1 = BigIntegerField(index=True)\n    f2 = BlobField()\n    f3 = BooleanField()\n    f4 = CharField(max_length=50)\n    f5 = DateField()\n    f6 = DateTimeField()\n    f7 = DecimalField()\n    f8 = DoubleField()\n    f9 = FloatField()\n    f10 = IntegerField(unique=True)\n    f11 = AutoField()\n    f12 = TextField()\n    f13 = TimeField()\n\n    class Meta:\n        indexes = (\n            (('f10', 'f11'), True),\n            (('f11', 'f8', 'f13'), False),\n        )\n\n\nclass Nullable(TestModel):\n    nullable_cf = CharField(null=True)\n    nullable_if = IntegerField(null=True)\n\n\nclass RelModel(TestModel):\n    col_types = ForeignKeyField(ColTypes, backref='foo')\n    col_types_nullable = ForeignKeyField(ColTypes, null=True)\n\n\nclass FKPK(TestModel):\n    col_types = ForeignKeyField(ColTypes, primary_key=True)\n\n\nclass Underscores(TestModel):\n    _id = AutoField()\n    _name = CharField()\n\n\nclass Category(TestModel):\n    name = CharField(max_length=10)\n    parent = ForeignKeyField('self', null=True)\n\n\nclass Nugget(TestModel):\n    category_id = ForeignKeyField(Category, column_name='category_id')\n    category = CharField()\n\n\nclass NoPK(TestModel):\n    data = CharField()\n    class Meta:\n        primary_key = False\n\n\nclass BaseReflectionTestCase(ModelTestCase):\n    def setUp(self):\n        super(BaseReflectionTestCase, self).setUp()\n        self.introspector = Introspector.from_database(self.database)\n\n\nclass TestReflection(BaseReflectionTestCase):\n    requires = [ColTypes, Nullable, RelModel, FKPK, Underscores, Category,\n                Nugget]\n\n    def test_generate_models(self):\n        models = self.introspector.generate_models()\n        self.assertTrue(set((\n            'category',\n            'col_types',\n            'fkpk',\n            'nugget',\n            'nullable',\n            'rel_model',\n            'underscores')).issubset(set(models)))\n\n        def assertIsInstance(obj, klass):\n            self.assertTrue(isinstance(obj, klass))\n\n        category = models['category']\n        self.assertEqual(\n            sorted(category._meta.fields),\n            ['id', 'name', 'parent'])\n        assertIsInstance(category.id, AutoField)\n        assertIsInstance(category.name, CharField)\n        assertIsInstance(category.parent, ForeignKeyField)\n        self.assertEqual(category.parent.rel_model, category)\n\n        fkpk = models['fkpk']\n        self.assertEqual(sorted(fkpk._meta.fields), ['col_types'])\n        assertIsInstance(fkpk.col_types, ForeignKeyField)\n        self.assertEqual(fkpk.col_types.rel_model, models['col_types'])\n        self.assertTrue(fkpk.col_types.primary_key)\n\n        relmodel = models['rel_model']\n        self.assertEqual(\n            sorted(relmodel._meta.fields),\n            ['col_types', 'col_types_nullable', 'id'])\n        assertIsInstance(relmodel.col_types, ForeignKeyField)\n        assertIsInstance(relmodel.col_types_nullable, ForeignKeyField)\n        self.assertFalse(relmodel.col_types.null)\n        self.assertTrue(relmodel.col_types_nullable.null)\n        self.assertEqual(relmodel.col_types.rel_model,\n                         models['col_types'])\n        self.assertEqual(relmodel.col_types_nullable.rel_model,\n                         models['col_types'])\n\n    @requires_sqlite\n    def test_generate_models_indexes(self):\n        models = self.introspector.generate_models()\n\n        self.assertEqual(models['fkpk']._meta.indexes, [])\n        self.assertEqual(models['rel_model']._meta.indexes, [])\n        self.assertEqual(models['category']._meta.indexes, [])\n\n        col_types = models['col_types']\n        indexed = set(['f1'])\n        unique = set(['f10'])\n        for field in col_types._meta.sorted_fields:\n            self.assertEqual(field.index, field.name in indexed)\n            self.assertEqual(field.unique, field.name in unique)\n        indexes = col_types._meta.indexes\n        self.assertEqual(sorted(indexes), [\n            (['f10', 'f11'], True),\n            (['f11', 'f8', 'f13'], False),\n        ])\n\n    def test_table_subset(self):\n        models = self.introspector.generate_models(table_names=[\n            'category',\n            'col_types',\n            'foobarbaz'])\n        self.assertEqual(sorted(models.keys()), ['category', 'col_types'])\n\n    @requires_sqlite\n    def test_sqlite_fk_re(self):\n        user_id_tests = [\n            'FOREIGN KEY(\"user_id\") REFERENCES \"users\"(\"id\")',\n            'FOREIGN KEY(user_id) REFERENCES users(id)',\n            'FOREIGN KEY  ([user_id])  REFERENCES  [users]  ([id])',\n            '\"user_id\" NOT NULL REFERENCES \"users\" (\"id\")',\n            'user_id not null references users (id)',\n        ]\n        fk_pk_tests = [\n            ('\"col_types_id\" INTEGER NOT NULL PRIMARY KEY REFERENCES '\n             '\"coltypes\" (\"f11\")'),\n            'FOREIGN KEY (\"col_types_id\") REFERENCES \"coltypes\" (\"f11\")',\n        ]\n        regex = SqliteMetadata.re_foreign_key\n\n        for test in user_id_tests:\n            match = re.search(regex, test, re.I)\n            self.assertEqual(match.groups(), (\n                'user_id', 'users', 'id',\n            ))\n\n        for test in fk_pk_tests:\n            match = re.search(regex, test, re.I)\n            self.assertEqual(match.groups(), (\n                'col_types_id', 'coltypes', 'f11',\n            ))\n\n    def test_make_column_name(self):\n        # Tests for is_foreign_key=False.\n        tests = (\n            ('Column', 'column'),\n            ('Foo_id', 'foo_id'),\n            ('foo_id', 'foo_id'),\n            ('foo_id_id', 'foo_id_id'),\n            ('foo', 'foo'),\n            ('_id', '_id'),\n            ('a123', 'a123'),\n            ('and', 'and_'),\n            ('Class', 'class_'),\n            ('Class_ID', 'class_id'),\n            ('camelCase', 'camel_case'),\n            ('ABCdefGhi', 'ab_cdef_ghi'),\n        )\n        for col_name, expected in tests:\n            self.assertEqual(\n                self.introspector.make_column_name(col_name), expected)\n\n        # Tests for is_foreign_key=True.\n        tests = (\n            ('Foo_id', 'foo'),\n            ('foo_id', 'foo'),\n            ('foo_id_id', 'foo_id'),\n            ('foo', 'foo'),\n            ('_id', '_id'),\n            ('a123', 'a123'),\n            ('and', 'and_'),\n            ('Class', 'class_'),\n            ('Class_ID', 'class_'),\n            ('camelCase', 'camel_case'),\n            ('ABCdefGhi', 'ab_cdef_ghi'),\n        )\n        for col_name, expected in tests:\n            self.assertEqual(\n                self.introspector.make_column_name(col_name, True), expected)\n\n    def test_make_model_name(self):\n        tests = (\n            ('Table', 'Table'),\n            ('table', 'Table'),\n            ('table_baz', 'TableBaz'),\n            ('foo__bar__baz2', 'FooBarBaz2'),\n            ('foo12_3', 'Foo123'),\n        )\n        for table_name, expected in tests:\n            self.assertEqual(\n                self.introspector.make_model_name(table_name), expected)\n\n    def test_col_types(self):\n        (columns,\n         primary_keys,\n         foreign_keys,\n         model_names,\n         indexes) = self.introspector.introspect()\n\n        expected = (\n            ('col_types', (\n                ('f1', (BigIntegerField, IntegerField), False),\n                # There do not appear to be separate constants for the blob and\n                # text field types in MySQL's drivers. See GH#1034.\n                ('f2', (BlobField, TextField), False),\n                ('f3', (BooleanField, IntegerField), False),\n                ('f4', CharField, False),\n                ('f5', DateField, False),\n                ('f6', DateTimeField, False),\n                ('f7', DecimalField, False),\n                ('f8', (DoubleField, FloatField), False),\n                ('f9', FloatField, False),\n                ('f10', IntegerField, False),\n                ('f11', AutoField, False),\n                ('f12', TextField, False),\n                ('f13', TimeField, False))),\n            ('rel_model', (\n                ('col_types_id', ForeignKeyField, False),\n                ('col_types_nullable_id', ForeignKeyField, True))),\n            ('nugget', (\n                ('category_id', ForeignKeyField, False),\n                ('category', CharField, False))),\n            ('nullable', (\n                ('nullable_cf', CharField, True),\n                ('nullable_if', IntegerField, True))),\n            ('fkpk', (\n                ('col_types_id', ForeignKeyField, False),)),\n            ('underscores', (\n                ('_id', AutoField, False),\n                ('_name', CharField, False))),\n            ('category', (\n                ('name', CharField, False),\n                ('parent_id', ForeignKeyField, True))),\n        )\n\n        for table_name, expected_columns in expected:\n            introspected_columns = columns[table_name]\n\n            for field_name, field_class, is_null in expected_columns:\n                if not isinstance(field_class, (list, tuple)):\n                    field_class = (field_class,)\n                column = introspected_columns[field_name]\n                self.assertTrue(column.field_class in field_class,\n                                \"%s in %s\" % (column.field_class, field_class))\n                self.assertEqual(column.nullable, is_null)\n\n    def test_foreign_keys(self):\n        (columns,\n         primary_keys,\n         foreign_keys,\n         model_names,\n         indexes) = self.introspector.introspect()\n\n        self.assertEqual(foreign_keys['col_types'], [])\n\n        rel_model = foreign_keys['rel_model']\n        self.assertEqual(len(rel_model), 2)\n\n        fkpk = foreign_keys['fkpk']\n        self.assertEqual(len(fkpk), 1)\n\n        fkpk_fk = fkpk[0]\n        self.assertEqual(fkpk_fk.table, 'fkpk')\n        self.assertEqual(fkpk_fk.column, 'col_types_id')\n        self.assertEqual(fkpk_fk.dest_table, 'col_types')\n        self.assertEqual(fkpk_fk.dest_column, 'f11')\n\n        category = foreign_keys['category']\n        self.assertEqual(len(category), 1)\n\n        category_fk = category[0]\n        self.assertEqual(category_fk.table, 'category')\n        self.assertEqual(category_fk.column, 'parent_id')\n        self.assertEqual(category_fk.dest_table, 'category')\n        self.assertEqual(category_fk.dest_column, 'id')\n\n    def test_table_names(self):\n        (columns,\n         primary_keys,\n         foreign_keys,\n         model_names,\n         indexes) = self.introspector.introspect()\n\n        names = (\n            ('col_types', 'ColTypes'),\n            ('nullable', 'Nullable'),\n            ('rel_model', 'RelModel'),\n            ('fkpk', 'Fkpk'))\n        for k, v in names:\n            self.assertEqual(model_names[k], v)\n\n    def test_column_meta(self):\n        (columns,\n         primary_keys,\n         foreign_keys,\n         model_names,\n         indexes) = self.introspector.introspect()\n\n        rel_model = columns['rel_model']\n\n        col_types_id = rel_model['col_types_id']\n        self.assertEqual(col_types_id.get_field_parameters(), {\n            'column_name': \"'col_types_id'\",\n            'model': 'ColTypes',\n            'field': \"'f11'\",\n        })\n\n        col_types_nullable_id = rel_model['col_types_nullable_id']\n        self.assertEqual(col_types_nullable_id.get_field_parameters(), {\n            'column_name': \"'col_types_nullable_id'\",\n            'null': True,\n            'backref': \"'col_types_col_types_nullable_set'\",\n            'model': 'ColTypes',\n            'field': \"'f11'\",\n        })\n\n        fkpk = columns['fkpk']\n        self.assertEqual(fkpk['col_types_id'].get_field_parameters(), {\n            'column_name': \"'col_types_id'\",\n            'model': 'ColTypes',\n            'primary_key': True,\n            'field': \"'f11'\"})\n\n        category = columns['category']\n\n        parent_id = category['parent_id']\n        self.assertEqual(parent_id.get_field_parameters(), {\n            'column_name': \"'parent_id'\",\n            'null': True,\n            'model': \"'self'\",\n            'field': \"'id'\",\n        })\n\n        nugget = columns['nugget']\n        category_fk = nugget['category_id']\n        self.assertEqual(category_fk.name, 'category_id')\n        self.assertEqual(category_fk.get_field_parameters(), {\n            'field': \"'id'\",\n            'model': 'Category',\n            'column_name': \"'category_id'\",\n        })\n\n        category = nugget['category']\n        self.assertEqual(category.name, 'category')\n\n    def test_get_field(self):\n        (columns,\n         primary_keys,\n         foreign_keys,\n         model_names,\n         indexes) = self.introspector.introspect()\n\n        expected = (\n            ('col_types', (\n                ('f1', ('f1 = BigIntegerField(index=True)',\n                        'f1 = IntegerField(index=True)')),\n                ('f2', ('f2 = BlobField()', 'f2 = TextField()')),\n                ('f4', 'f4 = CharField()'),\n                ('f5', 'f5 = DateField()'),\n                ('f6', 'f6 = DateTimeField()'),\n                ('f7', 'f7 = DecimalField()'),\n                ('f10', 'f10 = IntegerField(unique=True)'),\n                ('f11', 'f11 = AutoField()'),\n                ('f12', ('f12 = TextField()', 'f12 = BlobField()')),\n                ('f13', 'f13 = TimeField()'),\n            )),\n            ('nullable', (\n                ('nullable_cf', 'nullable_cf = '\n                 'CharField(null=True)'),\n                ('nullable_if', 'nullable_if = IntegerField(null=True)'),\n            )),\n            ('fkpk', (\n                ('col_types_id', 'col_types = ForeignKeyField('\n                 \"column_name='col_types_id', field='f11', model=ColTypes, \"\n                 'primary_key=True)'),\n            )),\n            ('nugget', (\n                ('category_id', 'category_id = ForeignKeyField('\n                 \"column_name='category_id', field='id', model=Category)\"),\n                ('category', 'category = CharField()'),\n            )),\n            ('rel_model', (\n                ('col_types_id', 'col_types = ForeignKeyField('\n                 \"column_name='col_types_id', field='f11', model=ColTypes)\"),\n                ('col_types_nullable_id', 'col_types_nullable = '\n                 \"ForeignKeyField(backref='col_types_col_types_nullable_set', \"\n                 \"column_name='col_types_nullable_id', field='f11', \"\n                 'model=ColTypes, null=True)'),\n            )),\n            ('underscores', (\n                ('_id', '_id = AutoField()'),\n                ('_name', '_name = CharField()'),\n            )),\n            ('category', (\n                ('name', 'name = CharField()'),\n                ('parent_id', 'parent = ForeignKeyField('\n                 \"column_name='parent_id', field='id', model='self', \"\n                 'null=True)'),\n            )),\n        )\n\n        for table, field_data in expected:\n            for field_name, fields in field_data:\n                if not isinstance(fields, tuple):\n                    fields = (fields,)\n                actual = columns[table][field_name].get_field()\n                self.assertTrue(actual in fields,\n                                '%s not in %s' % (actual, fields))\n\n\nclass TestReflectNoPK(BaseReflectionTestCase):\n    requires = [NoPK]\n\n    def test_no_pk(self):\n        models = self.introspector.generate_models()\n        NoPK = models['no_pk']\n        if IS_CRDB:\n            # CockroachDB always includes a \"rowid\".\n            self.assertEqual(NoPK._meta.sorted_field_names, ['rowid', 'data'])\n        else:\n            self.assertEqual(NoPK._meta.sorted_field_names, ['data'])\n            self.assertTrue(NoPK._meta.primary_key is False)\n\n\nclass EventLog(TestModel):\n    data = CharField(constraints=[SQL('DEFAULT \\'\\'')])\n    timestamp = DateTimeField(constraints=[SQL('DEFAULT current_timestamp')])\n    flags = IntegerField(constraints=[SQL('DEFAULT 0')])\n    misc = TextField(constraints=[SQL('DEFAULT \\'foo\\'')])\n\n\nclass DefaultVals(TestModel):\n    key = CharField(constraints=[SQL('DEFAULT \\'foo\\'')])\n    value = IntegerField(constraints=[SQL('DEFAULT 0')])\n\n    class Meta:\n        primary_key = CompositeKey('key', 'value')\n\n\nclass TestReflectDefaultValues(BaseReflectionTestCase):\n    requires = [DefaultVals, EventLog]\n\n    @requires_sqlite\n    def test_default_values(self):\n        models = self.introspector.generate_models()\n        default_vals = models['default_vals']\n\n        create_table = (\n            'CREATE TABLE IF NOT EXISTS \"default_vals\" ('\n            '\"key\" VARCHAR(255) NOT NULL DEFAULT \\'foo\\', '\n            '\"value\" INTEGER NOT NULL DEFAULT 0, '\n            'PRIMARY KEY (\"key\", \"value\"))')\n\n        # Re-create table using the introspected schema.\n        self.assertSQL(default_vals._schema._create_table(), create_table, [])\n        default_vals.drop_table()\n        default_vals.create_table()\n\n        # Verify that the introspected schema has not changed.\n        models = self.introspector.generate_models()\n        default_vals = models['default_vals']\n        self.assertSQL(default_vals._schema._create_table(), create_table, [])\n\n    @requires_sqlite\n    def test_default_values_extended(self):\n        models = self.introspector.generate_models()\n        eventlog = models['event_log']\n\n        create_table = (\n            'CREATE TABLE IF NOT EXISTS \"event_log\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"data\" VARCHAR(255) NOT NULL DEFAULT \\'\\', '\n            '\"timestamp\" DATETIME NOT NULL DEFAULT current_timestamp, '\n            '\"flags\" INTEGER NOT NULL DEFAULT 0, '\n            '\"misc\" TEXT NOT NULL DEFAULT \\'foo\\')')\n\n        # Re-create table using the introspected schema.\n        self.assertSQL(eventlog._schema._create_table(), create_table, [])\n        eventlog.drop_table()\n        eventlog.create_table()\n\n        # Verify that the introspected schema has not changed.\n        models = self.introspector.generate_models()\n        eventlog = models['event_log']\n        self.assertSQL(eventlog._schema._create_table(), create_table, [])\n\n\nclass TestReflectionDependencies(BaseReflectionTestCase):\n    requires = [User, Tweet]\n\n    def test_generate_dependencies(self):\n        models = self.introspector.generate_models(table_names=['tweet'])\n        self.assertEqual(set(models), set(('users', 'tweet')))\n\n        IUser = models['users']\n        ITweet = models['tweet']\n\n        self.assertEqual(set(ITweet._meta.fields), set((\n            'id', 'user', 'content', 'timestamp')))\n        self.assertEqual(set(IUser._meta.fields), set(('id', 'username')))\n        self.assertTrue(ITweet.user.rel_model is IUser)\n        self.assertTrue(ITweet.user.rel_field is IUser.id)\n\n    def test_ignore_backrefs(self):\n        models = self.introspector.generate_models(table_names=['users'])\n        self.assertEqual(set(models), set(('users',)))\n\n\nclass Note(TestModel):\n    content = TextField()\n    timestamp = DateTimeField(default=datetime.datetime.now)\n    status = IntegerField()\n\n\nclass TestReflectViews(BaseReflectionTestCase):\n    requires = [Note]\n\n    def setUp(self):\n        super(TestReflectViews, self).setUp()\n        self.database.execute_sql('CREATE VIEW notes_public AS '\n                                  'SELECT content, timestamp FROM note '\n                                  'WHERE status = 1 ORDER BY timestamp DESC')\n\n    def tearDown(self):\n        self.database.execute_sql('DROP VIEW notes_public')\n        super(TestReflectViews, self).tearDown()\n\n    def test_views_ignored_default(self):\n        models = self.introspector.generate_models()\n        self.assertFalse('notes_public' in models)\n\n    def test_introspect_view(self):\n        models = self.introspector.generate_models(include_views=True)\n        self.assertTrue('notes_public' in models)\n\n        NotesPublic = models['notes_public']\n        self.assertEqual(sorted(NotesPublic._meta.fields),\n                         ['content', 'timestamp'])\n        self.assertTrue(isinstance(NotesPublic.content, TextField))\n        self.assertTrue(isinstance(NotesPublic.timestamp, DateTimeField))\n\n    @skip_if(IS_SQLITE_OLD)\n    @skip_if(IS_CRDB, 'crdb does not respect order by in view def')\n    def test_introspect_view_integration(self):\n        for i, (ct, st) in enumerate([('n1', 1), ('n2', 2), ('n3', 1)]):\n            Note.create(content=ct, status=st,\n                        timestamp=datetime.datetime(2018, 1, 1 + i))\n\n        NP = self.introspector.generate_models(\n            table_names=['notes_public'], include_views=True)['notes_public']\n        self.assertEqual([(np.content, np.timestamp) for np in NP.select()], [\n            ('n3', datetime.datetime(2018, 1, 3)),\n            ('n1', datetime.datetime(2018, 1, 1))])\n\n\nclass TestCyclicalFK(BaseReflectionTestCase):\n    def setUp(self):\n        super(TestCyclicalFK, self).setUp()\n        warnings.filterwarnings('ignore')\n\n    @requires_sqlite\n    @skip_if(IS_CYSQLITE, 'cysqlite does not implement cursor at the moment.')\n    def test_cyclical_fk(self):\n        # NOTE: this schema was provided by a user.\n        cursor = self.database.cursor()\n        cursor.executescript(\n            'CREATE TABLE flow_run_state (id CHAR(36) NOT NULL, '\n            'flow_run_id CHAR(36) NOT NULL, '\n\t    'CONSTRAINT pk_flow_run_state PRIMARY KEY (id), '\n\t    'CONSTRAINT fk_flow_run_state__flow_run_id__flow_run '\n            'FOREIGN KEY(flow_run_id) REFERENCES flow_run (id) '\n            'ON DELETE cascade); '\n            'CREATE TABLE flow_run (id CHAR(36) NOT NULL, '\n\t    'state_id CHAR(36) NOT NULL, '\n\t    'CONSTRAINT pk_flow_run PRIMARY KEY (id), '\n\t    'CONSTRAINT fk_flow_run__state_id__flow_run_state '\n            'FOREIGN KEY(state_id) REFERENCES flow_run_state (id) '\n            'ON DELETE SET NULL);')\n        M = self.introspector.generate_models()\n        FRS = M['flow_run_state']\n        FR = M['flow_run']\n        self.assertEqual(sorted(FR._meta.fields), ['id', 'state'])\n        self.assertEqual(sorted(FRS._meta.fields), ['flow_run', 'id'])\n        self.assertTrue(isinstance(FR.id, CharField))\n        self.assertTrue(isinstance(FR.state, ForeignKeyField))\n        self.assertTrue(FR.state.rel_model is FRS)\n        self.assertTrue(isinstance(FRS.id, CharField))\n        self.assertTrue(isinstance(FRS.flow_run, ForeignKeyField))\n        self.assertTrue(FRS.flow_run.rel_model is FR)\n\n\nclass Event(TestModel):\n    key = TextField()\n    timestamp = DateTimeField(index=True)\n    metadata = TextField(default='')\n\n\nclass TestInteractiveHelpers(ModelTestCase):\n    requires = [Category, Event]\n\n    def test_generate_models(self):\n        M = generate_models(self.database)\n        self.assertTrue('category' in M)\n        self.assertTrue('event' in M)\n\n        def assertFields(m, expected):\n            actual = [(f.name, f.field_type) for f in m._meta.sorted_fields]\n            self.assertEqual(actual, expected)\n\n        assertFields(M['category'], [('id', 'AUTO'), ('name', 'VARCHAR'),\n                                     ('parent', 'INT')])\n        assertFields(M['event'], [\n            ('id', 'AUTO'),\n            ('key', 'TEXT'),\n            ('timestamp', 'DATETIME'),\n            ('metadata', 'TEXT')])\n"
  },
  {
    "path": "tests/regressions.py",
    "content": "import datetime\nimport json\nimport random\nimport threading\nimport time\nimport uuid\n\nfrom peewee import *\nfrom playhouse.hybrid import *\nfrom playhouse.migrate import migrate\nfrom playhouse.migrate import SchemaMigrator\nfrom playhouse.shortcuts import ThreadSafeDatabaseMetadata\n\nfrom .base import BaseTestCase\nfrom .base import IS_MYSQL\nfrom .base import IS_MYSQL_ADVANCED_FEATURES\nfrom .base import IS_SQLITE\nfrom .base import IS_SQLITE_OLD\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import get_in_memory_db\nfrom .base import requires_models\nfrom .base import requires_mysql\nfrom .base import requires_postgresql\nfrom .base import skip_if\nfrom .base import skip_unless\nfrom .base import slow_test\nfrom .base_models import Sample\nfrom .base_models import Tweet\nfrom .base_models import User\n\n\nclass ColAlias(TestModel):\n    name = TextField(column_name='pname')\n\n\nclass CARef(TestModel):\n    colalias = ForeignKeyField(ColAlias, backref='carefs', column_name='ca',\n                               object_id_name='colalias_id')\n\n\nclass TestQueryAliasToColumnName(ModelTestCase):\n    requires = [ColAlias, CARef]\n\n    def setUp(self):\n        super(TestQueryAliasToColumnName, self).setUp()\n        with self.database.atomic():\n            for name in ('huey', 'mickey'):\n                col_alias = ColAlias.create(name=name)\n                CARef.create(colalias=col_alias)\n\n    def test_alias_to_column_name(self):\n        # The issue here occurs when we take a field whose name differs from\n        # it's underlying column name, then alias that field to it's column\n        # name. In this case, peewee was *not* respecting the alias and using\n        # the field name instead.\n        query = (ColAlias\n                 .select(ColAlias.name.alias('pname'))\n                 .order_by(ColAlias.name))\n        self.assertEqual([c.pname for c in query], ['huey', 'mickey'])\n\n        # Ensure that when using dicts the logic is preserved.\n        query = query.dicts()\n        self.assertEqual([r['pname'] for r in query], ['huey', 'mickey'])\n\n    def test_alias_overlap_with_join(self):\n        query = (CARef\n                 .select(CARef, ColAlias.name.alias('pname'))\n                 .join(ColAlias)\n                 .order_by(ColAlias.name))\n        with self.assertQueryCount(1):\n            self.assertEqual([r.colalias.pname for r in query],\n                             ['huey', 'mickey'])\n\n        # Note: we cannot alias the join to \"ca\", as this is the object-id\n        # descriptor name.\n        query = (CARef\n                 .select(CARef, ColAlias.name.alias('pname'))\n                 .join(ColAlias,\n                       on=(CARef.colalias == ColAlias.id).alias('ca'))\n                 .order_by(ColAlias.name))\n        with self.assertQueryCount(1):\n            self.assertEqual([r.ca.pname for r in query], ['huey', 'mickey'])\n\n    def test_cannot_alias_join_to_object_id_name(self):\n        query = CARef.select(CARef, ColAlias.name.alias('pname'))\n        expr = (CARef.colalias == ColAlias.id).alias('colalias_id')\n        self.assertRaises(ValueError, query.join, ColAlias, on=expr)\n\n\nclass TestOverrideModelRepr(BaseTestCase):\n    def test_custom_reprs(self):\n        # In 3.5.0, Peewee included a new implementation and semantics for\n        # customizing model reprs. This introduced a regression where model\n        # classes that defined a __repr__() method had this override ignored\n        # silently. This test ensures that it is possible to completely\n        # override the model repr.\n        class Foo(Model):\n            def __repr__(self):\n                return 'FOO: %s' % self.id\n\n        f = Foo(id=1337)\n        self.assertEqual(repr(f), 'FOO: 1337')\n\n\nclass DiA(TestModel):\n    a = TextField(unique=True)\nclass DiB(TestModel):\n    a = ForeignKeyField(DiA)\n    b = TextField()\nclass DiC(TestModel):\n    b = ForeignKeyField(DiB)\n    c = TextField()\nclass DiD(TestModel):\n    c = ForeignKeyField(DiC)\n    d = TextField()\nclass DiBA(TestModel):\n    a = ForeignKeyField(DiA, to_field=DiA.a)\n    b = TextField()\n\n\nclass TestDeleteInstanceRegression(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [DiA, DiB, DiC, DiD, DiBA]\n\n    def test_delete_instance_regression(self):\n        with self.database.atomic():\n            a1, a2, a3 = [DiA.create(a=a) for a in ('a1', 'a2', 'a3')]\n            for a in (a1, a2, a3):\n                for j in (1, 2):\n                    b = DiB.create(a=a, b='%s-b%s' % (a.a, j))\n                    c = DiC.create(b=b, c='%s-c' % (b.b))\n                    d = DiD.create(c=c, d='%s-d' % (c.c))\n\n                    DiBA.create(a=a, b='%s-b%s' % (a.a, j))\n\n        # (a1 (b1 (c (d))), (b2 (c (d)))), (a2 ...), (a3 ...)\n        with self.assertQueryCount(5):\n            a2.delete_instance(recursive=True)\n\n        self.assertHistory(5, [\n            ('DELETE FROM \"di_d\" WHERE (\"di_d\".\"c_id\" IN ('\n             'SELECT \"t1\".\"id\" FROM \"di_c\" AS \"t1\" WHERE (\"t1\".\"b_id\" IN ('\n             'SELECT \"t2\".\"id\" FROM \"di_b\" AS \"t2\" WHERE (\"t2\".\"a_id\" = ?)'\n             '))))', [2]),\n            ('DELETE FROM \"di_c\" WHERE (\"di_c\".\"b_id\" IN ('\n             'SELECT \"t1\".\"id\" FROM \"di_b\" AS \"t1\" WHERE (\"t1\".\"a_id\" = ?)'\n             '))', [2]),\n            ('DELETE FROM \"di_ba\" WHERE (\"di_ba\".\"a_id\" = ?)', ['a2']),\n            ('DELETE FROM \"di_b\" WHERE (\"di_b\".\"a_id\" = ?)', [2]),\n            ('DELETE FROM \"di_a\" WHERE (\"di_a\".\"id\" = ?)', [2])\n        ])\n\n        # a1 & a3 exist, plus their relations.\n        self.assertTrue(DiA.select().count(), 2)\n        for rel in (DiB, DiBA, DiC, DiD):\n            self.assertTrue(rel.select().count(), 4)  # 2x2\n\n        with self.assertQueryCount(5):\n            a1.delete_instance(recursive=True)\n\n        # Only the objects related to a3 exist still.\n        self.assertTrue(DiA.select().count(), 1)\n        self.assertEqual(DiA.get(DiA.a == 'a3').id, a3.id)\n        self.assertEqual([d.d for d in DiD.select().order_by(DiD.d)],\n                         ['a3-b1-c-d', 'a3-b2-c-d'])\n        self.assertEqual([c.c for c in DiC.select().order_by(DiC.c)],\n                         ['a3-b1-c', 'a3-b2-c'])\n        self.assertEqual([b.b for b in DiB.select().order_by(DiB.b)],\n                         ['a3-b1', 'a3-b2'])\n        self.assertEqual([ba.b for ba in DiBA.select().order_by(DiBA.b)],\n                         ['a3-b1', 'a3-b2'])\n\n\nclass TestCountUnionRegression(ModelTestCase):\n    @requires_mysql\n    @requires_models(User)\n    def test_count_union(self):\n        with self.database.atomic():\n            for i in range(5):\n                User.create(username='user-%d' % i)\n\n        lhs = User.select()\n        rhs = User.select()\n        query = (lhs | rhs)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'UNION '\n            'SELECT \"t2\".\"id\", \"t2\".\"username\" FROM \"users\" AS \"t2\"'), [])\n\n        self.assertEqual(query.count(), 5)\n\n        query = query.limit(3)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'UNION '\n            'SELECT \"t2\".\"id\", \"t2\".\"username\" FROM \"users\" AS \"t2\" '\n            'LIMIT ?'), [3])\n        self.assertEqual(query.count(), 3)\n\n\nclass User2(TestModel):\n    username = TextField()\n\nclass Category2(TestModel):\n    name = TextField()\n    parent = ForeignKeyField('self', backref='children', null=True)\n    user = ForeignKeyField(User2)\n\n\nclass TestGithub1354(ModelTestCase):\n    @requires_models(Category2, User2)\n    def test_get_or_create_self_referential_fk2(self):\n        huey = User2.create(username='huey')\n        parent = Category2.create(name='parent', user=huey)\n        child, created = Category2.get_or_create(parent=parent, name='child',\n                                                 user=huey)\n        child_db = Category2.get(Category2.parent == parent)\n        self.assertEqual(child_db.user.username, 'huey')\n        self.assertEqual(child_db.parent.name, 'parent')\n        self.assertEqual(child_db.name, 'child')\n\n\nclass TestInsertFromSQL(ModelTestCase):\n    def setUp(self):\n        super(TestInsertFromSQL, self).setUp()\n\n        self.database.execute_sql('create table if not exists user_src '\n                                  '(name TEXT);')\n        tbl = Table('user_src').bind(self.database)\n        tbl.insert(name='foo').execute()\n\n    def tearDown(self):\n        super(TestInsertFromSQL, self).tearDown()\n        self.database.execute_sql('drop table if exists user_src')\n\n    @requires_models(User)\n    def test_insert_from_sql(self):\n        query_src = SQL('SELECT name FROM user_src')\n        User.insert_from(query=query_src, fields=[User.username]).execute()\n        self.assertEqual([u.username for u in User.select()], ['foo'])\n\n\nclass TestSubqueryFunctionCall(BaseTestCase):\n    def test_subquery_function_call(self):\n        Sample = Table('sample')\n        SA = Sample.alias('s2')\n        query = (Sample\n                 .select(Sample.c.data)\n                 .where(~fn.EXISTS(\n                     SA.select(SQL('1')).where(SA.c.key == 'foo'))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"data\" FROM \"sample\" AS \"t1\" '\n            'WHERE NOT EXISTS('\n            'SELECT 1 FROM \"sample\" AS \"s2\" WHERE (\"s2\".\"key\" = ?))'), ['foo'])\n\n\nclass A(TestModel):\n    id = IntegerField(primary_key=True)\nclass B(TestModel):\n    id = IntegerField(primary_key=True)\nclass C(TestModel):\n    id = IntegerField(primary_key=True)\n    a = ForeignKeyField(A)\n    b = ForeignKeyField(B)\n\nclass TestCrossJoin(ModelTestCase):\n    requires = [A, B, C]\n\n    def setUp(self):\n        super(TestCrossJoin, self).setUp()\n        A.insert_many([(1,), (2,), (3,)], fields=[A.id]).execute()\n        B.insert_many([(1,), (2,)], fields=[B.id]).execute()\n        C.insert_many([\n            (1, 1, 1),\n            (2, 1, 2),\n            (3, 2, 1)], fields=[C.id, C.a, C.b]).execute()\n\n    def test_cross_join(self):\n        query = (A\n                 .select(A.id.alias('aid'), B.id.alias('bid'))\n                 .join(B, JOIN.CROSS)\n                 .join(C, JOIN.LEFT_OUTER, on=(\n                     (C.a == A.id) &\n                     (C.b == B.id)))\n                 .where(C.id.is_null())\n                 .order_by(A.id, B.id))\n        self.assertEqual(list(query.tuples()), [(2, 2), (3, 1), (3, 2)])\n\n\ndef _create_users_tweets(db):\n    data = (\n        ('huey', ('meow', 'hiss', 'purr')),\n        ('mickey', ('woof', 'bark')),\n        ('zaizee', ()))\n    with db.atomic():\n        for username, tweets in data:\n            user = User.create(username=username)\n            for tweet in tweets:\n                Tweet.create(user=user, content=tweet)\n\n\nclass TestSubqueryInSelect(ModelTestCase):\n    requires = [User, Tweet]\n\n    def setUp(self):\n        super(TestSubqueryInSelect, self).setUp()\n        _create_users_tweets(self.database)\n\n    def test_subquery_in_select(self):\n        subq = User.select().where(User.username == 'huey')\n        query = (Tweet\n                 .select(Tweet.content, Tweet.user.in_(subq).alias('is_huey'))\n                 .order_by(Tweet.content))\n        self.assertEqual([(r.content, r.is_huey) for r in query], [\n            ('bark', False),\n            ('hiss', True),\n            ('meow', True),\n            ('purr', True),\n            ('woof', False)])\n\n\n@requires_postgresql\nclass TestReturningIntegrationRegressions(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_returning_integration_subqueries(self):\n        _create_users_tweets(self.database)\n\n        # We can use a correlated subquery in the RETURNING clause.\n        subq = (Tweet\n                .select(fn.COUNT(Tweet.id).alias('ct'))\n                .where(Tweet.user == User.id))\n        query = (User\n                 .update(username=(User.username + '-x'))\n                 .returning(subq.alias('ct'), User.username))\n        result = query.execute()\n        self.assertEqual(sorted([(r.ct, r.username) for r in result]), [\n            (0, 'zaizee-x'), (2, 'mickey-x'), (3, 'huey-x')])\n\n        # We can use a correlated subquery via UPDATE...FROM, and reference the\n        # FROM table in both the update and the RETURNING clause.\n        subq = (User\n                .select(User.id, fn.COUNT(Tweet.id).alias('ct'))\n                .join(Tweet, JOIN.LEFT_OUTER)\n                .group_by(User.id))\n        query = (User\n                 .update(username=User.username + subq.c.ct)\n                 .from_(subq)\n                 .where(User.id == subq.c.id)\n                 .returning(subq.c.ct, User.username))\n        result = query.execute()\n        self.assertEqual(sorted([(r.ct, r.username) for r in result]), [\n            (0, 'zaizee-x0'), (2, 'mickey-x2'), (3, 'huey-x3')])\n\n    def test_returning_integration(self):\n        query = (User\n                 .insert_many([('huey',), ('mickey',), ('zaizee',)],\n                              fields=[User.username])\n                 .returning(User.id, User.username)\n                 .objects())\n        result = query.execute()\n        self.assertEqual([(r.id, r.username) for r in result], [\n            (1, 'huey'), (2, 'mickey'), (3, 'zaizee')])\n\n        query = (User\n                 .delete()\n                 .where(~User.username.startswith('h'))\n                 .returning(User.id, User.username)\n                 .objects())\n        result = query.execute()\n        self.assertEqual(sorted([(r.id, r.username) for r in result]), [\n            (2, 'mickey'), (3, 'zaizee')])\n\n\nclass TestUpdateIntegrationRegressions(ModelTestCase):\n    requires = [User, Tweet, Sample]\n\n    def setUp(self):\n        super(TestUpdateIntegrationRegressions, self).setUp()\n        _create_users_tweets(self.database)\n        for i in range(4):\n            Sample.create(counter=i, value=i)\n\n    @skip_if(IS_MYSQL)\n    def test_update_examples(self):\n        # Do a simple update.\n        res = (User\n               .update(username=(User.username + '-cat'))\n               .where(User.username != 'mickey')\n               .execute())\n\n        users = User.select().order_by(User.username)\n        self.assertEqual([u.username for u in users.clone()],\n                         ['huey-cat', 'mickey', 'zaizee-cat'])\n\n        # Do an update using a subquery..\n        subq = User.select(User.username).where(User.username == 'mickey')\n        res = (User\n               .update(username=(User.username + '-dog'))\n               .where(User.username.in_(subq))\n               .execute())\n        self.assertEqual([u.username for u in users.clone()],\n                         ['huey-cat', 'mickey-dog', 'zaizee-cat'])\n\n        # Subquery referring to a different table.\n        subq = User.select().where(User.username == 'mickey-dog')\n        res = (Tweet\n               .update(content=(Tweet.content + '-x'))\n               .where(Tweet.user.in_(subq))\n               .execute())\n\n        self.assertEqual(\n            [t.content for t in Tweet.select().order_by(Tweet.id)],\n            ['meow', 'hiss', 'purr', 'woof-x', 'bark-x'])\n\n        # Subquery on the right-hand of the assignment.\n        subq = (Tweet\n                .select(fn.COUNT(Tweet.id).cast('text'))\n                .where(Tweet.user == User.id))\n        res = User.update(username=(User.username + '-' + subq)).execute()\n\n        self.assertEqual([u.username for u in users.clone()],\n                         ['huey-cat-3', 'mickey-dog-2', 'zaizee-cat-0'])\n\n    def test_update_examples_2(self):\n        SA = Sample.alias()\n        subq = (SA\n                .select(SA.value)\n                .where(SA.value.in_([1.0, 3.0])))\n        res = (Sample\n               .update(counter=(Sample.counter + Sample.value.cast('int')))\n               .where(Sample.value.in_(subq))\n               .execute())\n\n        query = (Sample\n                 .select(Sample.counter, Sample.value)\n                 .order_by(Sample.id)\n                 .tuples())\n        self.assertEqual(list(query.clone()), [(0, 0.), (2, 1.), (2, 2.),\n                                               (6, 3.)])\n\n        subq = (SA\n                .select(SA.counter - SA.value.cast('int'))\n                .where(SA.value == Sample.value))\n        res = (Sample\n               .update(counter=subq)\n               .where(Sample.value.in_([1., 3.]))\n               .execute())\n        self.assertEqual(list(query.clone()), [(0, 0.), (1, 1.), (2, 2.),\n                                               (3, 3.)])\n\nclass TestSelectValueConversion(ModelTestCase):\n    requires = [User]\n\n    @skip_if(IS_SQLITE_OLD or IS_MYSQL)\n    def test_select_value_conversion(self):\n        u1 = User.create(username='u1')\n        cte = User.select(User.id.cast('text')).cte('tmp', columns=('id',))\n\n        query = User.select(cte.c.id.alias('id')).with_cte(cte).from_(cte)\n        u1_id, = [user.id for user in query]\n        self.assertEqual(u1_id, u1.id)\n\n        query2 = User.select(cte.c.id.coerce(False)).with_cte(cte).from_(cte)\n        u1_id, = [user.id for user in query2]\n        self.assertEqual(u1_id, str(u1.id))\n\n\nclass ConflictDetectedException(Exception): pass\n\nclass BaseVersionedModel(TestModel):\n    version = IntegerField(default=1, index=True)\n\n    def save_optimistic(self):\n        if not self.id:\n            # This is a new record, so the default logic is to perform an\n            # INSERT. Ideally your model would also have a unique\n            # constraint that made it impossible for two INSERTs to happen\n            # at the same time.\n            return self.save()\n\n        # Update any data that has changed and bump the version counter.\n        field_data = dict(self.__data__)\n        current_version = field_data.pop('version', 1)\n        self._populate_unsaved_relations(field_data)\n        field_data = self._prune_fields(field_data, self.dirty_fields)\n        if not field_data:\n            raise ValueError('No changes have been made.')\n\n        ModelClass = type(self)\n        field_data['version'] = ModelClass.version + 1  # Atomic increment.\n\n        query = ModelClass.update(**field_data).where(\n            (ModelClass.version == current_version) &\n            (ModelClass.id == self.id))\n        if query.execute() == 0:\n            # No rows were updated, indicating another process has saved\n            # a new version. How you handle this situation is up to you,\n            # but for simplicity I'm just raising an exception.\n            raise ConflictDetectedException()\n        else:\n            # Increment local version to match what is now in the db.\n            self.version += 1\n            return True\n\nclass VUser(BaseVersionedModel):\n    username = TextField()\n\nclass VTweet(BaseVersionedModel):\n    user = ForeignKeyField(VUser, null=True)\n    content = TextField()\n\n\nclass TestOptimisticLockingDemo(ModelTestCase):\n    requires = [VUser, VTweet]\n\n    def test_optimistic_locking(self):\n        vu = VUser(username='u1')\n        vu.save_optimistic()\n        vt = VTweet(user=vu, content='t1')\n        vt.save_optimistic()\n\n        # Update the \"vt\" row in the db, which bumps the version counter.\n        vt2 = VTweet.get(VTweet.id == vt.id)\n        vt2.content = 't1-x'\n        vt2.save_optimistic()\n\n        # Since no data was modified, this returns a ValueError.\n        self.assertRaises(ValueError, vt.save_optimistic)\n\n        # If we do make an update and attempt to save, a conflict is detected.\n        vt.content = 't1-y'\n        self.assertRaises(ConflictDetectedException, vt.save_optimistic)\n        self.assertEqual(vt.version, 1)\n\n        vt_db = VTweet.get(VTweet.id == vt.id)\n        self.assertEqual(vt_db.content, 't1-x')\n        self.assertEqual(vt_db.version, 2)\n        self.assertEqual(vt_db.user.username, 'u1')\n\n    def test_optimistic_locking_populate_fks(self):\n        vt = VTweet(content='t1')\n        vt.save_optimistic()\n\n        vu = VUser(username='u1')\n        vt.user = vu\n\n        vu.save_optimistic()\n        vt.save_optimistic()\n        vt_db = VTweet.get(VTweet.content == 't1')\n        self.assertEqual(vt_db.version, 2)\n        self.assertEqual(vt_db.user.username, 'u1')\n\n\nclass TS(TestModel):\n    key = CharField(primary_key=True)\n    timestamp = TimestampField(utc=True)\n\n\nclass TestZeroTimestamp(ModelTestCase):\n    requires = [TS]\n\n    def test_zero_timestamp(self):\n        t0 = TS.create(key='t0', timestamp=0)\n        t1 = TS.create(key='t1', timestamp=1)\n\n        t0_db = TS.get(TS.key == 't0')\n        self.assertEqual(t0_db.timestamp, datetime.datetime(1970, 1, 1))\n\n        t1_db = TS.get(TS.key == 't1')\n        self.assertEqual(t1_db.timestamp,\n                         datetime.datetime(1970, 1, 1, 0, 0, 1))\n\n\nclass Player(TestModel):\n    name = TextField()\n\nclass Game(TestModel):\n    name = TextField()\n    player = ForeignKeyField(Player)\n\nclass Score(TestModel):\n    game = ForeignKeyField(Game)\n    points = IntegerField()\n\n\nclass TestJoinSubqueryAggregateViaLeftOuter(ModelTestCase):\n    requires = [Player, Game, Score]\n\n    def test_join_subquery_aggregate_left_outer(self):\n        with self.database.atomic():\n            p1, p2 = [Player.create(name=name) for name in ('p1', 'p2')]\n            games = []\n            for p in (p1, p2):\n                for gnum in (1, 2):\n                    g = Game.create(name='%s-g%s' % (p.name, gnum), player=p)\n                    games.append(g)\n\n            score_list = (\n                (10, 20, 30),\n                (),\n                (100, 110, 100),\n                (50, 50))\n            for g, plist in zip(games, score_list):\n                for p in plist:\n                    Score.create(game=g, points=p)\n\n        subq = (Game\n                .select(Game.player, fn.SUM(Score.points).alias('ptotal'),\n                        fn.AVG(Score.points).alias('pavg'))\n                .join(Score, JOIN.LEFT_OUTER)\n                .group_by(Game.player))\n        query = (Player\n                 .select(Player, subq.c.ptotal, subq.c.pavg)\n                 .join(subq, on=(Player.id == subq.c.player_id))\n                 .order_by(Player.name))\n\n        with self.assertQueryCount(1):\n            results = [(p.name, p.game.ptotal, p.game.pavg) for p in query]\n\n        self.assertEqual(results, [('p1', 60, 20), ('p2', 410, 82)])\n\n        with self.assertQueryCount(1):\n            obj_query = query.objects()\n            results = [(p.name, p.ptotal, p.pavg) for p in obj_query]\n\n        self.assertEqual(results, [('p1', 60, 20), ('p2', 410, 82)])\n\n\nclass Project(TestModel):\n    name = TextField()\n\nclass Task(TestModel):\n    name = TextField()\n    project = ForeignKeyField(Project, backref='tasks')\n    alt = ForeignKeyField(Project, backref='alt_tasks')\n\n\nclass TestModelGraphMultiFK(ModelTestCase):\n    requires = [Project, Task]\n\n    def test_model_graph_multi_fk(self):\n        pa, pb, pc = [Project.create(name=name) for name in 'abc']\n        t1 = Task.create(name='t1', project=pa, alt=pc)\n        t2 = Task.create(name='t2', project=pb, alt=pb)\n\n        P1 = Project.alias('p1')\n        P2 = Project.alias('p2')\n        LO = JOIN.LEFT_OUTER\n\n        # Query using join expression.\n        q1 = (Task\n              .select(Task, P1, P2)\n              .join_from(Task, P1, LO, on=(Task.project == P1.id))\n              .join_from(Task, P2, LO, on=(Task.alt == P2.id))\n              .order_by(Task.name))\n\n        # Query specifying target field.\n        q2 = (Task\n              .select(Task, P1, P2)\n              .join_from(Task, P1, LO, on=Task.project)\n              .join_from(Task, P2, LO, on=Task.alt)\n              .order_by(Task.name))\n\n        # Query specifying with missing target field.\n        q3 = (Task\n              .select(Task, P1, P2)\n              .join_from(Task, P1, LO)\n              .join_from(Task, P2, LO, on=Task.alt)\n              .order_by(Task.name))\n\n        for query in (q1, q2, q3):\n            with self.assertQueryCount(1):\n                t1, t2 = list(query)\n                self.assertEqual(t1.project.name, 'a')\n                self.assertEqual(t1.alt.name, 'c')\n                self.assertEqual(t2.project.name, 'b')\n                self.assertEqual(t2.alt.name, 'b')\n\n\nclass TestBlobFieldContextRegression(BaseTestCase):\n    def test_blob_field_context_regression(self):\n        class A(Model):\n            f = BlobField()\n\n        orig = A.f._constructor\n        db = get_in_memory_db()\n        with db.bind_ctx([A]):\n            self.assertTrue(A.f._constructor is db.get_binary_type())\n\n        self.assertTrue(A.f._constructor is orig)\n\n\nclass Product(TestModel):\n    id = CharField()\n    color = CharField()\n    class Meta:\n        primary_key = CompositeKey('id', 'color')\n\nclass Sku(TestModel):\n    upc = CharField(primary_key=True)\n    product_id = CharField()\n    color = CharField()\n    class Meta:\n        constraints = [SQL('FOREIGN KEY (product_id, color) REFERENCES '\n                           'product(id, color)')]\n\n    @hybrid_property\n    def product(self):\n        if not hasattr(self, '_product'):\n            self._product = Product.get((Product.id == self.product_id) &\n                                        (Product.color == self.color))\n        return self._product\n\n    @product.setter\n    def product(self, obj):\n        self._product = obj\n        self.product_id = obj.id\n        self.color = obj.color\n\n    @product.expression\n    def product(cls):\n        return (Product.id == cls.product_id) & (Product.color == cls.color)\n\n\nclass TestFKCompositePK(ModelTestCase):\n    requires = [Product, Sku]\n\n    def test_fk_composite_pk_regression(self):\n        Product.insert_many([\n            (1, 'red'),\n            (1, 'blue'),\n            (2, 'red'),\n            (2, 'green'),\n            (3, 'white')]).execute()\n        Sku.insert_many([\n            ('1-red', 1, 'red'),\n            ('1-blue', 1, 'blue'),\n            ('2-red', 2, 'red'),\n            ('2-green', 2, 'green'),\n            ('3-white', 3, 'white')]).execute()\n\n        query = (Product\n                 .select(Product, Sku)\n                 .join(Sku, on=Sku.product)\n                 .where(Product.color == 'red')\n                 .order_by(Product.id, Product.color))\n        with self.assertQueryCount(1):\n            rows = [(p.id, p.color, p.sku.upc) for p in query]\n            self.assertEqual(rows, [\n                ('1', 'red', '1-red'),\n                ('2', 'red', '2-red')])\n\n        query = (Sku\n                 .select(Sku, Product)\n                 .join(Product, on=Sku.product)\n                 .where(Product.color != 'red')\n                 .order_by(Sku.upc))\n        with self.assertQueryCount(1):\n            rows = [(s.upc, s.product_id, s.color,\n                     s.product.id, s.product.color) for s in query]\n            self.assertEqual(rows, [\n                ('1-blue', '1', 'blue', '1', 'blue'),\n                ('2-green', '2', 'green', '2', 'green'),\n                ('3-white', '3', 'white', '3', 'white')])\n\n\nclass RS(TestModel):\n    name = TextField()\n\nclass RD(TestModel):\n    key = TextField()\n    value = IntegerField()\n    rs = ForeignKeyField(RS, backref='rds')\n\nclass RKV(TestModel):\n    key = CharField(max_length=10)\n    value = IntegerField()\n    extra = IntegerField()\n    class Meta:\n        primary_key = CompositeKey('key', 'value')\n\n\nclass TestRegressionCountDistinct(ModelTestCase):\n    @requires_models(RS, RD)\n    def test_regression_count_distinct(self):\n        rs = RS.create(name='rs')\n\n        nums = [0, 1, 2, 3, 2, 1, 0]\n        RD.insert_many([('k%s' % i, i, rs) for i in nums]).execute()\n\n        query = RD.select(RD.key).distinct()\n        self.assertEqual(query.count(), 4)\n\n        # Try re-selecting using the id/key, which are all distinct.\n        query = query.select(RD.id, RD.key)\n        self.assertEqual(query.count(), 7)\n\n        # Re-select the key/value, of which there are 4 distinct.\n        query = query.select(RD.key, RD.value)\n        self.assertEqual(query.count(), 4)\n\n        query = rs.rds.select(RD.key).distinct()\n        self.assertEqual(query.count(), 4)\n\n        query = rs.rds.select(RD.key, RD.value).distinct()\n        self.assertEqual(query.count(), 4)  # Was returning 7!\n\n    @requires_models(RKV)\n    def test_regression_count_distinct_cpk(self):\n        RKV.insert_many([('k%s' % i, i, i) for i in range(5)]).execute()\n        self.assertEqual(RKV.select().distinct().count(), 5)\n\n\nclass TestReselectModelRegression(ModelTestCase):\n    requires = [User]\n\n    def test_reselect_model_regression(self):\n        u1, u2, u3 = [User.create(username='u%s' % i) for i in '123']\n\n        query = User.select(User.username).order_by(User.username.desc())\n        self.assertEqual(list(query.tuples()), [('u3',), ('u2',), ('u1',)])\n\n        query = query.select(User)\n        self.assertEqual(list(query.tuples()), [\n            (u3.id, 'u3',),\n            (u2.id, 'u2',),\n            (u1.id, 'u1',)])\n\n\nclass TestJoinCorrelatedSubquery(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_join_correlated_subquery(self):\n        for i in range(3):\n            user = User.create(username='u%s' % i)\n            for j in range(i + 1):\n                Tweet.create(user=user, content='u%s-%s' % (i, j))\n\n        UA = User.alias()\n        subq = (UA\n                .select(UA.username)\n                .where(UA.username.in_(('u0', 'u2'))))\n\n        query = (Tweet\n                 .select(Tweet, User)\n                 .join(User, on=(\n                     (Tweet.user == User.id) &\n                     (User.username.in_(subq))))\n                 .order_by(Tweet.id))\n\n        with self.assertQueryCount(1):\n            data = [(t.content, t.user.username) for t in query]\n            self.assertEqual(data, [\n                ('u0-0', 'u0'),\n                ('u2-0', 'u2'),\n                ('u2-1', 'u2'),\n                ('u2-2', 'u2')])\n\n\nclass RU(TestModel):\n    username = TextField()\n\n\nclass Recipe(TestModel):\n    name = TextField()\n    created_by = ForeignKeyField(RU, backref='recipes')\n    changed_by = ForeignKeyField(RU, backref='recipes_modified')\n\n\nclass TestMultiFKJoinRegression(ModelTestCase):\n    requires = [RU, Recipe]\n\n    def test_multi_fk_join_regression(self):\n        u1, u2 = [RU.create(username=u) for u in ('u1', 'u2')]\n        for (n, a, m) in (('r11', u1, u1), ('r12', u1, u2), ('r21', u2, u1)):\n            Recipe.create(name=n, created_by=a, changed_by=m)\n\n        Change = RU.alias()\n        query = (Recipe\n                 .select(Recipe, RU, Change)\n                 .join(RU, on=(RU.id == Recipe.created_by).alias('a'))\n                 .switch(Recipe)\n                 .join(Change, on=(Change.id == Recipe.changed_by).alias('b'))\n                 .order_by(Recipe.name))\n        with self.assertQueryCount(1):\n            data = [(r.name, r.a.username, r.b.username) for r in query]\n            self.assertEqual(data, [\n                ('r11', 'u1', 'u1'),\n                ('r12', 'u1', 'u2'),\n                ('r21', 'u2', 'u1')])\n\n\nclass TestCompoundExistsRegression(ModelTestCase):\n    requires = [User]\n\n    def test_compound_regressions_1961(self):\n        UA = User.alias()\n        cq = (User.select(User.id) | UA.select(UA.id))\n        # Calling .exists() fails with AttributeError, no attribute \"columns\".\n        self.assertFalse(cq.exists())\n        self.assertEqual(cq.count(), 0)\n\n        User.create(username='u1')\n        self.assertTrue(cq.exists())\n        self.assertEqual(cq.count(), 1)\n\n\nclass TestViewFieldMapping(ModelTestCase):\n    requires = [User]\n\n    def tearDown(self):\n        try:\n            self.execute('drop view user_testview_fm')\n        except Exception as exc:\n            pass\n        super(TestViewFieldMapping, self).tearDown()\n\n    def test_view_field_mapping(self):\n        user = User.create(username='huey')\n        self.execute('create view user_testview_fm as '\n                     'select id, username from users')\n\n        class View(User):\n            class Meta:\n                table_name = 'user_testview_fm'\n\n        self.assertEqual([(v.id, v.username) for v in View.select()],\n                         [(user.id, 'huey')])\n\n\nclass TC(TestModel):\n    ifield = IntegerField()\n    ffield = FloatField()\n    cfield = TextField()\n    tfield = TextField()\n\n\nclass TestTypeCoercion(ModelTestCase):\n    requires = [TC]\n\n    def test_type_coercion(self):\n        t = TC.create(ifield='10', ffield='20.5', cfield=30, tfield=40)\n        t_db = TC.get(TC.id == t.id)\n\n        self.assertEqual(t_db.ifield, 10)\n        self.assertEqual(t_db.ffield, 20.5)\n        self.assertEqual(t_db.cfield, '30')\n        self.assertEqual(t_db.tfield, '40')\n\n\nclass TestLikeColumnValue(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_like_column_value(self):\n        # e.g., find all tweets that contain the users own username.\n        u1, u2, u3 = [User.create(username='u%s' % i) for i in (1, 2, 3)]\n        data = (\n            (u1, ('nada', 'i am u1', 'u1 is my name')),\n            (u2, ('nothing', 'he is u1')),\n            (u3, ('she is u2', 'hey u3 is me', 'xx')))\n        for user, tweets in data:\n            Tweet.insert_many([(user, tweet) for tweet in tweets],\n                              fields=[Tweet.user, Tweet.content]).execute()\n\n        expressions = (\n            (Tweet.content ** ('%' + User.username + '%')),\n            Tweet.content.contains(User.username))\n\n        for expr in expressions:\n            query = (Tweet\n                     .select(Tweet, User)\n                     .join(User)\n                     .where(expr)\n                     .order_by(Tweet.id))\n\n            self.assertEqual([(t.user.username, t.content) for t in query], [\n                ('u1', 'i am u1'),\n                ('u1', 'u1 is my name'),\n                ('u3', 'hey u3 is me')])\n\n\nclass TestUnionParenthesesRegression(ModelTestCase):\n    requires = [User]\n\n    def test_union_parentheses_regression(self):\n        ua, ub, uc = [User.create(username=u) for u in 'abc']\n        lhs = User.select(User.id).where(User.username == 'a')\n        rhs = User.select(User.id).where(User.username == 'c')\n        union = lhs.union_all(rhs)\n        self.assertEqual(sorted([u.id for u in union]), [ua.id, uc.id])\n\n        query = User.select().where(User.id.in_(union)).order_by(User.id)\n        self.assertEqual([u.username for u in query], ['a', 'c'])\n\n\nclass NoPK(TestModel):\n    data = IntegerField()\n    class Meta:\n        primary_key = False\n\n\nclass TestNoPKHashRegression(ModelTestCase):\n    requires = [NoPK]\n\n    def test_no_pk_hash_regression(self):\n        npk = NoPK.create(data=1)\n        npk_db = NoPK.get(NoPK.data == 1)\n        # When a model does not define a primary key, we cannot test equality.\n        self.assertTrue(npk != npk_db)\n\n        # Their hash is the same, though they are not equal.\n        self.assertEqual(hash(npk), hash(npk_db))\n\n\nclass Site(TestModel):\n    url = TextField()\n\nclass Page(TestModel):\n    site = ForeignKeyField(Site, backref='pages')\n    title = TextField()\n\nclass PageItem(TestModel):\n    page = ForeignKeyField(Page, backref='items')\n    content = TextField()\n\n\nclass TestModelFilterJoinOrdering(ModelTestCase):\n    requires = [Site, Page, PageItem]\n\n    def setUp(self):\n        super(TestModelFilterJoinOrdering, self).setUp()\n        with self.database.atomic():\n            s1, s2 = [Site.create(url=s) for s in ('s1', 's2')]\n            p11, p12, p21 = [Page.create(site=s, title=t) for s, t in\n                             ((s1, 'p1-1'), (s1, 'p1-2'), (s2, 'p2-1'))]\n            items = (\n                (p11, 's1p1i1'),\n                (p11, 's1p1i2'),\n                (p11, 's1p1i3'),\n                (p12, 's1p2i1'),\n                (p21, 's2p1i1'))\n            PageItem.insert_many(items).execute()\n\n    def test_model_filter_join_ordering(self):\n        q = PageItem.filter(page__site__url='s1').order_by(PageItem.content)\n        self.assertSQL(q, (\n            'SELECT \"t1\".\"id\", \"t1\".\"page_id\", \"t1\".\"content\" '\n            'FROM \"page_item\" AS \"t1\" '\n            'INNER JOIN \"page\" AS \"t2\" ON (\"t1\".\"page_id\" = \"t2\".\"id\") '\n            'INNER JOIN \"site\" AS \"t3\" ON (\"t2\".\"site_id\" = \"t3\".\"id\") '\n            'WHERE (\"t3\".\"url\" = ?) ORDER BY \"t1\".\"content\"'), ['s1'])\n\n        def assertQ(q):\n            with self.assertQueryCount(1):\n                self.assertEqual([pi.content for pi in q],\n                                 ['s1p1i1', 's1p1i2', 's1p1i3', 's1p2i1'])\n\n        assertQ(q)\n\n        sid = Site.get(Site.url == 's1').id\n        q = (PageItem\n             .filter(page__site__url='s1', page__site__id=sid)\n             .order_by(PageItem.content))\n        assertQ(q)\n\n        q = (PageItem\n             .filter(page__site__id=sid)\n             .filter(page__site__url='s1')\n             .order_by(PageItem.content))\n        assertQ(q)\n\n        q = (PageItem\n             .filter(page__site__id=sid)\n             .filter(DQ(page__title='p1-1') | DQ(page__title='p1-2'))\n             .filter(page__site__url='s1')\n             .order_by(PageItem.content))\n        assertQ(q)\n\n\nclass JsonField(TextField):\n    def db_value(self, value):\n        return json.dumps(value) if value is not None else None\n    def python_value(self, value):\n        if value is not None:\n            return json.loads(value)\n\nclass JM(TestModel):\n    key = TextField()\n    data = JsonField()\n\n\nclass TestListValueConversion(ModelTestCase):\n    requires = [JM]\n\n    def test_list_value_conversion(self):\n        jm = JM.create(key='k1', data=['i0', 'i1'])\n        jm.key = 'k1-x'\n        jm.save()\n\n        jm_db = JM.get(JM.key == 'k1-x')\n        self.assertEqual(jm_db.data, ['i0', 'i1'])\n\n        JM.update(data=['i1', 'i2']).execute()\n        jm_db = JM.get(JM.key == 'k1-x')\n        self.assertEqual(jm_db.data, ['i1', 'i2'])\n\n        jm2 = JM.create(key='k2', data=['i3', 'i4'])\n\n        jm_db.data = ['i1', 'i2', 'i3']\n        jm2.data = ['i4', 'i5']\n\n        JM.bulk_update([jm_db, jm2], fields=[JM.key, JM.data])\n\n        jm = JM.get(JM.key == 'k1-x')\n        self.assertEqual(jm.data, ['i1', 'i2', 'i3'])\n        jm2 = JM.get(JM.key == 'k2')\n        self.assertEqual(jm2.data, ['i4', 'i5'])\n\n\nclass TestCountSubqueryEquals(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_count_subquery_equals(self):\n        a, b, c = [User.create(username=u) for u in 'abc']\n        Tweet.insert_many([(a, 'a1'), (b, 'b1')]).execute()\n\n        subq = (Tweet\n                .select(fn.COUNT(Tweet.id))\n                .where(Tweet.user == User.id))\n        query = User.select().where(subq == 0)\n        self.assertEqual([u.username for u in query], ['c'])\n\n\nclass BoolModel(TestModel):\n    key = TextField()\n    active = BooleanField()\n\n\nclass TestBooleanCompare(ModelTestCase):\n    requires = [BoolModel]\n\n    def test_boolean_compare(self):\n        b1 = BoolModel.create(key='b1', active=True)\n        b2 = BoolModel.create(key='b2', active=False)\n\n        expr2key = (\n            ((BoolModel.active == True), 'b1'),\n            ((BoolModel.active == False), 'b2'),\n            ((BoolModel.active != True), 'b2'),\n            ((BoolModel.active != False), 'b1'))\n        for expr, key in expr2key:\n            q = BoolModel.select().where(expr)\n            self.assertEqual([b.key for b in q], [key])\n\n\nclass CPK(TestModel):\n    name = TextField()\n\nclass CPKFK(TestModel):\n    key = CharField()\n    cpk = ForeignKeyField(CPK)\n    class Meta:\n        primary_key = CompositeKey('key', 'cpk')\n\n\nclass TestCompositePKwithFK(ModelTestCase):\n    requires = [CPK, CPKFK]\n\n    def test_composite_pk_with_fk(self):\n        c1 = CPK.create(name='c1')\n        c2 = CPK.create(name='c2')\n        CPKFK.create(key='k1', cpk=c1)\n        CPKFK.create(key='k2', cpk=c1)\n        CPKFK.create(key='k3', cpk=c2)\n\n        query = (CPKFK\n                 .select(CPKFK.key, CPK)\n                 .join(CPK)\n                 .order_by(CPKFK.key, CPK.name))\n        with self.assertQueryCount(1):\n            self.assertEqual([(r.key, r.cpk.name) for r in query],\n                             [('k1', 'c1'), ('k2', 'c1'), ('k3', 'c2')])\n\n\nclass TestChainWhere(ModelTestCase):\n    requires = [User]\n\n    def test_chain_where(self):\n        for username in 'abcd':\n            User.create(username=username)\n\n        q = (User.select()\n             .where(User.username != 'a')\n             .where(User.username != 'd')\n             .order_by(User.username))\n        self.assertEqual([u.username for u in q], ['b', 'c'])\n\n        q = (User.select()\n             .where(User.username != 'a')\n             .where(User.username != 'd')\n             .where(User.username == 'b'))\n        self.assertEqual([u.username for u in q], ['b'])\n\n\nclass BCUser(TestModel):\n    username = CharField(unique=True)\n\nclass BCTweet(TestModel):\n    user = ForeignKeyField(BCUser, field=BCUser.username)\n    content = TextField()\n\n\nclass TestBulkCreateWithFK(ModelTestCase):\n    @requires_models(BCUser, BCTweet)\n    def test_bulk_create_with_fk(self):\n        u1 = BCUser.create(username='u1')\n        u2 = BCUser.create(username='u2')\n        with self.assertQueryCount(1):\n            BCTweet.bulk_create([\n                BCTweet(user='u1', content='t%s' % i)\n                for i in range(4)])\n\n        self.assertEqual(BCTweet.select().where(BCTweet.user == 'u1').count(), 4)\n        self.assertEqual(BCTweet.select().where(BCTweet.user != 'u1').count(), 0)\n\n        u = BCUser(username='u3')\n        t = BCTweet(user=u, content='tx')\n        with self.assertQueryCount(2):\n            BCUser.bulk_create([u])\n            BCTweet.bulk_create([t])\n\n        with self.assertQueryCount(1):\n            t_db = (BCTweet\n                    .select(BCTweet, BCUser)\n                    .join(BCUser)\n                    .where(BCUser.username == 'u3')\n                    .get())\n            self.assertEqual(t_db.content, 'tx')\n            self.assertEqual(t_db.user.username, 'u3')\n\n    @requires_postgresql\n    @requires_models(User, Tweet)\n    def test_bulk_create_related_objects(self):\n        u = User(username='u1')\n        t = Tweet(user=u, content='t1')\n        with self.assertQueryCount(2):\n            User.bulk_create([u])\n            Tweet.bulk_create([t])\n\n        with self.assertQueryCount(1):\n            t_db = Tweet.select(Tweet, User).join(User).get()\n            self.assertEqual(t_db.content, 't1')\n            self.assertEqual(t_db.user.username, 'u1')\n\n\nclass UUIDReg(TestModel):\n    id = UUIDField(primary_key=True, default=uuid.uuid4)\n    key = TextField()\n\nclass CharPKKV(TestModel):\n    id = CharField(primary_key=True)\n    key = TextField()\n    value = IntegerField(default=0)\n\n\nclass TestBulkUpdateNonIntegerPK(ModelTestCase):\n    @requires_models(UUIDReg)\n    def test_bulk_update_uuid_pk(self):\n        r1 = UUIDReg.create(key='k1')\n        r2 = UUIDReg.create(key='k2')\n        r1.key = 'k1-x'\n        r2.key = 'k2-x'\n        UUIDReg.bulk_update((r1, r2), (UUIDReg.key,))\n\n        r1_db, r2_db = UUIDReg.select().order_by(UUIDReg.key)\n        self.assertEqual(r1_db.key, 'k1-x')\n        self.assertEqual(r2_db.key, 'k2-x')\n\n    @requires_models(CharPKKV)\n    def test_bulk_update_non_integer_pk(self):\n        a, b, c = [CharPKKV.create(id=c, key='k%s' % c) for c in 'abc']\n        a.key = 'ka-x'\n        a.value = 1\n        b.value = 2\n        c.key = 'kc-x'\n        c.value = 3\n        CharPKKV.bulk_update((a, b, c), (CharPKKV.key, CharPKKV.value))\n\n        data = list(CharPKKV.select().order_by(CharPKKV.id).tuples())\n        self.assertEqual(data, [\n            ('a', 'ka-x', 1),\n            ('b', 'kb', 2),\n            ('c', 'kc-x', 3)])\n\n\nclass TestSaveClearingPK(ModelTestCase):\n    requires = [User, Tweet]\n\n    def test_save_clear_pk(self):\n        u = User.create(username='u1')\n        t1 = Tweet.create(content='t1', user=u)\n        orig_id, t1.id = t1.id, None\n        t1.content = 't2'\n        t1.save()\n        self.assertTrue(t1.id is not None)\n        self.assertTrue(t1.id != orig_id)\n        tweets = [t.content for t in u.tweets.order_by(Tweet.id)]\n        self.assertEqual(tweets, ['t1', 't2'])\n\n\nclass Bits(TestModel):\n    b1 = BitField(default=1)\n    b1_1 = b1.flag(1)\n    b1_2 = b1.flag(2)\n\n    b2 = BitField(default=0)\n    b2_1 = b2.flag()\n    b2_2 = b2.flag()\n\n\nclass TestBitFieldName(ModelTestCase):\n    requires = [Bits]\n\n    def assertBits(self, bf, expected):\n        b1_1, b1_2, b2_1, b2_2 = expected\n        self.assertEqual(bf.b1_1, b1_1)\n        self.assertEqual(bf.b1_2, b1_2)\n        self.assertEqual(bf.b2_1, b2_1)\n        self.assertEqual(bf.b2_2, b2_2)\n\n    def test_bit_field_name(self):\n        bf = Bits.create()\n        self.assertBits(bf, (True, False, False, False))\n\n        bf.b1_1 = False\n        bf.b1_2 = True\n        bf.b2_1 = True\n        bf.save()\n        self.assertBits(bf, (False, True, True, False))\n\n        bf = Bits.get(Bits.id == bf.id)\n        self.assertBits(bf, (False, True, True, False))\n\n        self.assertEqual(bf.b1, 2)\n        self.assertEqual(bf.b2, 1)\n\n        self.assertEqual(Bits.select().where(Bits.b1_2).count(), 1)\n        self.assertEqual(Bits.select().where(Bits.b2_2).count(), 0)\n\n\nclass FKMA(TestModel):\n    name = TextField()\n\nclass FKMB(TestModel):\n    name = TextField()\n    fkma = ForeignKeyField(FKMA, backref='fkmb_set', null=True)\n\n\nclass TestFKMigrationRegression(ModelTestCase):\n    requires = [FKMA, FKMB]\n\n    def test_fk_migration(self):\n        migrator = SchemaMigrator.from_database(self.database)\n        kw = {'legacy': True} if IS_SQLITE else {}\n        migrate(migrator.drop_column(\n            FKMB._meta.table_name,\n            FKMB.fkma.column_name, **kw))\n\n        migrate(migrator.add_column(\n            FKMB._meta.table_name,\n            FKMB.fkma.column_name,\n            FKMB.fkma))\n\n        fa = FKMA.create(name='fa')\n        FKMB.create(name='fb', fkma=fa)\n        obj = FKMB.select().first()\n        self.assertEqual(obj.name, 'fb')\n\n\nclass ModelTypeField(CharField):\n    def db_value(self, value):\n        if value is not None:\n            return value._meta.name\n    def python_value(self, value):\n        if value is not None:\n            return {'user': User, 'tweet': Tweet}[value]\n\n\nclass MTF(TestModel):\n    name = TextField()\n    mtype = ModelTypeField()\n\n\nclass TestFieldValueRegression(ModelTestCase):\n    requires = [MTF]\n\n    def test_field_value_regression(self):\n        u = MTF.create(name='user', mtype=User)\n        u_db = MTF.get()\n\n        self.assertEqual(u_db.name, 'user')\n        self.assertTrue(u_db.mtype is User)\n\n\nclass NLM(TestModel):\n    a = IntegerField()\n    b = IntegerField()\n\nclass TestRegressionNodeListClone(ModelTestCase):\n    requires = [NLM]\n\n    def test_node_list_clone_expr(self):\n        expr = (NLM.a + NLM.b)\n        query = NLM.select(expr.alias('expr')).order_by(expr).distinct(expr)\n        self.assertSQL(query, (\n            'SELECT DISTINCT ON (\"t1\".\"a\" + \"t1\".\"b\") '\n            '(\"t1\".\"a\" + \"t1\".\"b\") AS \"expr\" '\n            'FROM \"nlm\" AS \"t1\" '\n            'ORDER BY (\"t1\".\"a\" + \"t1\".\"b\")'), [])\n\n\nclass LK(TestModel):\n    key = TextField()\n\nclass TestLikeEscape(ModelTestCase):\n    requires = [LK]\n\n    def assertNames(self, expr, expected):\n        query = LK.select().where(expr).order_by(LK.id)\n        self.assertEqual([lk.key for lk in query], expected)\n\n    def test_like_escape(self):\n        names = ('foo', 'foo%', 'foo%bar', 'foo_bar', 'fooxba', 'fooba')\n        LK.insert_many([(n,) for n in names]).execute()\n\n        cases = (\n            (LK.key.contains('bar'), ['foo%bar', 'foo_bar']),\n            (LK.key.contains('%'), ['foo%', 'foo%bar']),\n            (LK.key.contains('_'), ['foo_bar']),\n            (LK.key.contains('o%b'), ['foo%bar']),\n            (LK.key.startswith('foo%'), ['foo%', 'foo%bar']),\n            (LK.key.startswith('foo_'), ['foo_bar']),\n            (LK.key.startswith('bar'), []),\n            (LK.key.endswith('ba'), ['fooxba', 'fooba']),\n            (LK.key.endswith('_bar'), ['foo_bar']),\n            (LK.key.endswith('fo'), []),\n        )\n        for expr, expected in cases:\n            self.assertNames(expr, expected)\n\n    def test_like_escape_backslash(self):\n        names = ('foo_bar\\\\baz', 'bar\\\\', 'fbar\\\\baz', 'foo_bar')\n        LK.insert_many([(n,) for n in names]).execute()\n\n        cases = (\n            (LK.key.contains('\\\\'), ['foo_bar\\\\baz', 'bar\\\\', 'fbar\\\\baz']),\n            (LK.key.contains('_bar\\\\'), ['foo_bar\\\\baz']),\n            (LK.key.contains('bar\\\\'), ['foo_bar\\\\baz', 'bar\\\\', 'fbar\\\\baz']),\n        )\n        for expr, expected in cases:\n            self.assertNames(expr, expected)\n\n\nclass FKF_A(TestModel):\n    key = CharField(max_length=16, unique=True)\n\nclass FKF_B(TestModel):\n    fk_a_1 = ForeignKeyField(FKF_A, field='key')\n    fk_a_2 = IntegerField()\n\n\nclass TestQueryWithModelInstanceParam(ModelTestCase):\n    requires = [FKF_A, FKF_B]\n\n    def test_query_with_model_instance_param(self):\n        a1 = FKF_A.create(key='k1')\n        a2 = FKF_A.create(key='k2')\n        b1 = FKF_B.create(fk_a_1=a1, fk_a_2=a1)\n        b2 = FKF_B.create(fk_a_1=a2, fk_a_2=a2)\n\n        # Ensure that UPDATE works as expected as well.\n        b1.save()\n\n        # See also keys.TestFKtoNonPKField test, which replicates much of this.\n        args = (b1.fk_a_1, b1.fk_a_1_id, a1, a1.key)\n        for arg in args:\n            query = FKF_B.select().where(FKF_B.fk_a_1 == arg)\n            self.assertSQL(query, (\n                'SELECT \"t1\".\"id\", \"t1\".\"fk_a_1_id\", \"t1\".\"fk_a_2\" '\n                'FROM \"fkf_b\" AS \"t1\" '\n                'WHERE (\"t1\".\"fk_a_1_id\" = ?)'), ['k1'])\n            b1_db = query.get()\n            self.assertEqual(b1_db.id, b1.id)\n\n        # When we are handed a model instance and a conversion (an IntegerField\n        # in this case), when the attempted conversion fails we fall back to\n        # using the given model's primary-key.\n        args = (b1.fk_a_2, a1, a1.id)\n        for arg in args:\n            query = FKF_B.select().where(FKF_B.fk_a_2 == arg)\n            self.assertSQL(query, (\n                'SELECT \"t1\".\"id\", \"t1\".\"fk_a_1_id\", \"t1\".\"fk_a_2\" '\n                'FROM \"fkf_b\" AS \"t1\" '\n                'WHERE (\"t1\".\"fk_a_2\" = ?)'), [a1.id])\n            b1_db = query.get()\n            self.assertEqual(b1_db.id, b1.id)\n\n\n@skip_if(IS_SQLITE_OLD or IS_MYSQL)\nclass TestModelSelectFromSubquery(ModelTestCase):\n    requires = [User]\n\n    def test_model_select_from_subquery(self):\n        for i in range(5):\n            User.create(username='u%s' % i)\n\n        UA = User.alias()\n        subquery = (UA.select()\n                    .where(UA.username.in_(('u0', 'u2', 'u4'))))\n\n        cte = (ValuesList([('u0',), ('u4',)], columns=['username'])\n               .cte('user_cte', columns=['username']))\n\n        query = (User\n                 .select(subquery.c.id, subquery.c.username)\n                 .from_(subquery)\n                 .join(cte, on=(subquery.c.username == cte.c.username))\n                 .with_cte(cte)\n                 .order_by(subquery.c.username.desc()))\n        self.assertEqual([u.username for u in query], ['u4', 'u0'])\n        self.assertTrue(isinstance(query[0], User))\n\n\nclass CharPK(TestModel):\n    id = CharField(primary_key=True)\n    name = CharField(unique=True)\n\n    def __str__(self):\n        return self.name\n\nclass CharFK(TestModel):\n    id = IntegerField(primary_key=True)\n    cpk = ForeignKeyField(CharPK, field=CharPK.name)\n\n\nclass TestModelConversionRegression(ModelTestCase):\n    requires = [CharPK, CharFK]\n\n    def test_model_conversion_regression(self):\n        cpks = [CharPK.create(id=str(i), name='u%s' % i) for i in range(3)]\n\n        query = CharPK.select().where(CharPK.id << cpks)\n        self.assertEqual(sorted([c.id for c in query]), ['0', '1', '2'])\n\n        query = CharPK.select().where(CharPK.id.in_(list(CharPK.select())))\n        self.assertEqual(sorted([c.id for c in query]), ['0', '1', '2'])\n\n    def test_model_conversion_fk_retained(self):\n        cpks = [CharPK.create(id=str(i), name='u%s' % i) for i in range(3)]\n        cfks = [CharFK.create(id=i + 1, cpk='u%s' % i) for i in range(3)]\n\n        c0, c1, c2 = cpks\n        query = CharFK.select().where(CharFK.cpk << [c0, c2])\n        self.assertEqual(sorted([f.id for f in query]), [1, 3])\n\n\nclass FKN_A(TestModel): pass\nclass FKN_B(TestModel):\n    a = ForeignKeyField(FKN_A, null=True)\n\nclass TestSetFKNull(ModelTestCase):\n    requires = [FKN_A, FKN_B]\n\n    def test_set_fk_null(self):\n        a1 = FKN_A.create()\n        a2 = FKN_A()\n        b1 = FKN_B(a=a1)\n        b2 = FKN_B(a=a2)\n\n        self.assertTrue(b1.a is a1)\n        self.assertTrue(b2.a is a2)\n        b1.a = b2.a = None\n        self.assertTrue(b1.a is None)\n        self.assertTrue(b2.a is None)\n\n\nclass TestWeirdAliases(ModelTestCase):\n    requires = [User]\n\n    @skip_if(IS_MYSQL)  # mysql can't do anything normally.\n    def test_weird_aliases(self):\n        User.create(username='huey')\n        def assertAlias(s, expected):\n            query = User.select(s).dicts()\n            row = query[0]\n            self.assertEqual(list(row)[0], expected)\n\n        # When we explicitly provide an alias, use that.\n        assertAlias(User.username.alias('\"username\"'), '\"username\"')\n        assertAlias(User.username.alias('(username)'), '(username)')\n        assertAlias(User.username.alias('user(name)'), 'user(name)')\n        assertAlias(User.username.alias('(username\"'), '(username\"')\n        assertAlias(User.username.alias('\"username)'), '\"username)')\n        assertAlias(fn.LOWER(User.username).alias('user (name)'), 'user (name)')\n\n        # Here peewee cannot tell that an alias was given, so it will attempt\n        # to clean-up the column name returned by the cursor description.\n        assertAlias(SQL('\"t1\".\"username\" AS \"user name\"'), 'user name')\n        assertAlias(SQL('\"t1\".\"username\" AS \"user (name)\"'), 'user (name')\n        assertAlias(SQL('\"t1\".\"username\" AS \"(username)\"'), 'username')\n        assertAlias(SQL('\"t1\".\"username\" AS \"x.y.(username)\"'), 'username')\n        if IS_SQLITE:\n            assertAlias(SQL('LOWER(\"t1\".\"username\")'), 'username')\n\n\nclass NDF(TestModel):\n    key = CharField(primary_key=True)\n    date = DateTimeField(null=True)\n\nclass TestBulkUpdateAllNull(ModelTestCase):\n    requires = [NDF]\n\n    @skip_unless(IS_SQLITE or IS_MYSQL, 'postgres cannot do this properly')\n    def test_bulk_update_all_null(self):\n        n1 = NDF.create(key='n1', date=datetime.datetime(2021, 1, 1))\n        n2 = NDF.create(key='n2', date=datetime.datetime(2021, 1, 2))\n        rows = [NDF(key=key, date=None) for key in ('n1', 'n2')]\n        NDF.bulk_update(rows, fields=['date'])\n\n        query = NDF.select().order_by(NDF.key).tuples()\n        self.assertEqual([r for r in query], [('n1', None), ('n2', None)])\n\n\nclass CQA(TestModel):\n    a = TextField()\n    b = TextField()\n\n\nclass TestSelectFromUnion(ModelTestCase):\n    requires = [CQA]\n\n    def test_select_from_union(self):\n        CQA.insert_many([('a%d' % i, 'b%d' % i) for i in range(10)]).execute()\n\n        q1 = CQA.select(CQA.a).order_by(CQA.id).limit(3)\n        q2 = CQA.select(CQA.b).order_by(CQA.id).limit(3)\n\n        wq1 = q1.select_from(SQL('*'))\n        wq2 = q2.select_from(SQL('*'))\n        union = wq1 | wq2\n        data = [val for val, in union.tuples()]\n        self.assertEqual(sorted(data), ['a0', 'a1', 'a2', 'b0', 'b1', 'b2'])\n\n\nclass DF(TestModel):\n    name = TextField()\n    value = IntegerField()\nclass DFC(TestModel):\n    df = ForeignKeyField(DF)\n    name = TextField()\n    value = IntegerField()\nclass DFGC(TestModel):\n    dfc = ForeignKeyField(DFC)\n    name = TextField()\n    value = IntegerField()\n\n\nclass TestDjangoFilterRegression(ModelTestCase):\n    requires = [DF, DFC, DFGC]\n\n    def test_django_filter_regression(self):\n        a, b, c = [DF.create(name=n, value=i) for i, n in enumerate('abc')]\n        ca1 = DFC.create(df=a, name='a1', value=11)\n        ca2 = DFC.create(df=a, name='a2', value=12)\n        cb1 = DFC.create(df=b, name='b1', value=21)\n\n        gca1_1 = DFGC.create(dfc=ca1, name='a1-1', value=101)\n        gca1_2 = DFGC.create(dfc=ca1, name='a1-2', value=101)\n        gca2_1 = DFGC.create(dfc=ca2, name='a2-1', value=111)\n\n        def assertNames(q, expected):\n            self.assertEqual(sorted([n.name for n in q]), expected)\n\n        assertNames(DF.filter(name='a'), ['a'])\n        assertNames(DF.filter(name='a', id=a.id), ['a'])\n        assertNames(DF.filter(name__in=['a', 'c']), ['a', 'c'])\n        assertNames(DF.filter(name__in=['a', 'c'], id=a.id), ['a'])\n        assertNames(DF.filter(dfc_set__name='a1'), ['a'])\n        assertNames(DF.filter(dfc_set__name__in=['a1', 'b1']), ['a', 'b'])\n        assertNames(DF.filter(DQ(dfc_set__name='a1') | DQ(dfc_set__name='b1')),\n                    ['a', 'b'])\n        assertNames(DF.filter(dfc_set__dfgc_set__name='a1-1'), ['a'])\n        assertNames(DF.filter(\n            DQ(dfc_set__dfgc_set__name='a1-1') |\n            DQ(dfc_set__dfgc_set__name__in=['x', 'y'])), ['a'])\n\n        assertNames(DFC.filter(df__name='a'), ['a1', 'a2'])\n        assertNames(DFC.filter(df__name='a', value=11), ['a1'])\n        assertNames(DFC.filter(DQ(df__name='a') | DQ(df__name='b')),\n                    ['a1', 'a2', 'b1'])\n        assertNames(DFC.filter(\n            DQ(df__name='a') | DQ(dfgc_set__name='a1-1')).distinct(),\n            ['a1', 'a2'])\n\n        assertNames(DFGC.filter(dfc__df__name='a'), ['a1-1', 'a1-2', 'a2-1'])\n        assertNames(DFGC.filter(dfc__df__name='a', dfc__name='a2'), ['a2-1'])\n        assertNames(DFGC.filter(\n            DQ(dfc__df__value__lte=0) |\n            DQ(dfc__df__name='a', dfc__name='a1') |\n            DQ(dfc__name='a2')), ['a1-1', 'a1-2', 'a2-1'])\n\n        assertNames(\n            (DFGC.filter(DQ(dfc__df__value__lte=10) | DQ(dfc__value__lte=101))\n             .filter(DQ(name__ilike='a1%') | DQ(dfc__value=101))),\n            ['a1-1', 'a1-2'])\n\n        assertNames(DFGC.filter(dfc__df=a), ['a1-1', 'a1-2', 'a2-1'])\n        assertNames(DFGC.filter(dfc__df=a.id), ['a1-1', 'a1-2', 'a2-1'])\n\n        q = DFC.select().join(DF)\n        assertNames(q.filter(df=a), ['a1', 'a2'])\n        assertNames(q.filter(df__name='a'), ['a1', 'a2'])\n\n        DFA = DF.alias()\n        DFCA = DFC.alias()\n        DFGCA = DFGC.alias()\n        q = DFCA.select().join(DFA)\n        assertNames(q.filter(df=a), ['a1', 'a2'])\n        assertNames(q.filter(df__name='a'), ['a1', 'a2'])\n\n        q = DFGC.select().join(DFC).join(DF)\n        assertNames(q.filter(dfc__df=a), ['a1-1', 'a1-2', 'a2-1'])\n\n        q = DFGCA.select().join(DFCA).join(DFA)\n        assertNames(q.filter(dfc__df=a), ['a1-1', 'a1-2', 'a2-1'])\n\n        q = DF.select().join(DFC).join(DFGC)\n        assertNames(q.filter(dfc_set__dfgc_set__name='a1-1'), ['a'])\n\n\nclass TestFunctionInfiniteLoop(BaseTestCase):\n    def test_function_infinite_loop(self):\n        self.assertRaises(TypeError, lambda: list(fn.COUNT()))\n\n\nclass State(TestModel):\n    name = TextField()\nclass Transition(TestModel):\n    src = ForeignKeyField(State, backref='sources')\n    dest = ForeignKeyField(State, backref='dests')\n\nclass TestJoinTypePrefetchMultipleFKs(ModelTestCase):\n    requires = [State, Transition]\n\n    def test_join_prefetch_multiple_fks(self):\n        s1, s2a, s2b, s3 = [State.create(name=s)\n                            for s in ('s1', 's2a', 's2b', 's3')]\n        t1 = Transition.create(src=s1, dest=s2a)\n        t2 = Transition.create(src=s1, dest=s2b)\n        t3 = Transition.create(src=s2a, dest=s3)\n        t4 = Transition.create(src=s2b, dest=s3)\n\n        query = State.select().where(State.name != 's3').order_by(State.name)\n        transitions = (Transition\n                       .select(Transition, State)\n                       .join(State, on=Transition.dest)\n                       .order_by(Transition.id))\n        with self.assertQueryCount(2):\n            p = prefetch(query, transitions, prefetch_type=PREFETCH_TYPE.JOIN)\n            accum = []\n            for row in p:\n                accum.append((row.name, row.sources, row.dests,\n                              [d.dest.name for d in row.sources],\n                              [d.src.name for d in row.dests]))\n\n        self.assertEqual(accum, [\n            ('s1', [t1, t2], [], ['s2a', 's2b'], []),\n            ('s2a', [t3], [t1], ['s3'], ['s1']),\n            ('s2b', [t4], [t2], ['s3'], ['s1'])])\n\n\n@slow_test()\nclass TestThreadSafetyDecorators(ModelTestCase):\n    requires = [User]\n\n    def test_thread_safety_atomic(self):\n        @self.database.atomic()\n        def get_one(n):\n            time.sleep(n)\n            return User.select().first()\n        def run(n):\n            with self.database.atomic():\n                get_one(n)\n        User.create(username='u')\n        threads = [threading.Thread(target=run, args=(i,))\n                   for i in (0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.5)]\n        for t in threads: t.start()\n        for t in threads: t.join()\n\n\nclass TestQueryCountList(ModelTestCase):\n    requires = [User]\n\n    def test_iteration_single_query(self):\n        with self.assertQueryCount(1):\n            sq = User.select()\n            for i in range(3):\n                self.assertEqual(list(sq), [])\n                self.assertFalse(bool(sq))\n        with self.assertQueryCount(1):\n            sq = User.select().tuples()\n            for i in range(3):\n                self.assertEqual(list(sq), [])\n                self.assertFalse(bool(sq))\n        with self.assertQueryCount(1):\n            self.assertEqual(User.select().count(), 0)\n\n\nclass TestSumCaseSubquery(ModelTestCase):\n    requires = [Sample]\n\n    def test_sum_case_subquery(self):\n        Sample.insert_many([(i, i) for i in range(5)]).execute()\n\n        subq = Sample.select().where(Sample.counter.in_([1, 3, 5]))\n        case = Case(None, [(Sample.id.in_(subq), Sample.value)], 0)\n        q = Sample.select(fn.SUM(case))\n        self.assertEqual(q.scalar(), 4.0)\n\n\nclass I(TestModel):\n    name = TextField()\nclass S(TestModel):\n    i = ForeignKeyField(I)\nclass P(TestModel):\n    i = ForeignKeyField(I)\nclass PS(TestModel):\n    p = ForeignKeyField(P)\n    s = ForeignKeyField(S)\nclass PP(TestModel):\n    ps = ForeignKeyField(PS)\nclass O(TestModel):\n    ps = ForeignKeyField(PS)\n    s = ForeignKeyField(S)\nclass OX(TestModel):\n    o = ForeignKeyField(O, null=True)\n\nclass Character(TestModel):\n    name = TextField()\nclass Shape(TestModel):\n    character = ForeignKeyField(Character, null=True)\nclass ShapeDetail(TestModel):\n    shape = ForeignKeyField(Shape)\n\nclass TestDeleteInstanceDFS(ModelTestCase):\n    @requires_models(Character, Shape, ShapeDetail)\n    def test_delete_instance_dfs_nullable(self):\n        c1, c2 = [Character.create(name=name) for name in ('c1', 'c2')]\n        for c in (c1, c2):\n            s = Shape.create(character=c)\n            ShapeDetail.create(shape=s)\n\n        # Update nullables.\n        with self.assertQueryCount(2):\n            c1.delete_instance(True)\n\n        self.assertHistory(2, [\n            ('UPDATE \"shape\" SET \"character_id\" = ? WHERE '\n             '(\"shape\".\"character_id\" = ?)', [None, c1.id]),\n            ('DELETE FROM \"character\" WHERE (\"character\".\"id\" = ?)', [c1.id])])\n\n        self.assertEqual(Shape.select().count(), 2)\n\n        # Delete nullables as well.\n        with self.assertQueryCount(3):\n            c2.delete_instance(True, True)\n\n        self.assertHistory(3, [\n            ('DELETE FROM \"shape_detail\" WHERE '\n             '(\"shape_detail\".\"shape_id\" IN '\n             '(SELECT \"t1\".\"id\" FROM \"shape\" AS \"t1\" WHERE '\n             '(\"t1\".\"character_id\" = ?)))', [c2.id]),\n            ('DELETE FROM \"shape\" WHERE (\"shape\".\"character_id\" = ?)', [c2.id]),\n            ('DELETE FROM \"character\" WHERE (\"character\".\"id\" = ?)', [c2.id])])\n\n        self.assertEqual(Shape.select().count(), 1)\n\n    @requires_models(I, S, P, PS, PP, O, OX)\n    def test_delete_instance_dfs(self):\n        i1, i2 = [I.create(name=n) for n in ('i1', 'i2')]\n        for i in (i1, i2):\n            s = S.create(i=i)\n            p = P.create(i=i)\n            ps = PS.create(p=p, s=s)\n            pp = PP.create(ps=ps)\n            o = O.create(ps=ps, s=s)\n            ox = OX.create(o=o)\n\n        with self.assertQueryCount(9):\n            i1.delete_instance(recursive=True)\n\n        self.assertHistory(9, [\n            ('DELETE FROM \"pp\" WHERE ('\n             '\"pp\".\"ps_id\" IN (SELECT \"t1\".\"id\" FROM \"ps\" AS \"t1\" WHERE ('\n             '\"t1\".\"p_id\" IN (SELECT \"t2\".\"id\" FROM \"p\" AS \"t2\" WHERE ('\n             '\"t2\".\"i_id\" = ?)))))', [i1.id]),\n            ('UPDATE \"ox\" SET \"o_id\" = ? WHERE ('\n             '\"ox\".\"o_id\" IN (SELECT \"t1\".\"id\" FROM \"o\" AS \"t1\" WHERE ('\n             '\"t1\".\"ps_id\" IN (SELECT \"t2\".\"id\" FROM \"ps\" AS \"t2\" WHERE ('\n             '\"t2\".\"p_id\" IN (SELECT \"t3\".\"id\" FROM \"p\" AS \"t3\" WHERE ('\n             '\"t3\".\"i_id\" = ?)))))))', [None, i1.id]),\n            ('DELETE FROM \"o\" WHERE ('\n             '\"o\".\"ps_id\" IN (SELECT \"t1\".\"id\" FROM \"ps\" AS \"t1\" WHERE ('\n             '\"t1\".\"p_id\" IN (SELECT \"t2\".\"id\" FROM \"p\" AS \"t2\" WHERE ('\n             '\"t2\".\"i_id\" = ?)))))', [i1.id]),\n            ('DELETE FROM \"o\" WHERE ('\n             '\"o\".\"s_id\" IN (SELECT \"t1\".\"id\" FROM \"s\" AS \"t1\" WHERE ('\n             '\"t1\".\"i_id\" = ?)))', [i1.id]),\n            ('DELETE FROM \"ps\" WHERE ('\n             '\"ps\".\"p_id\" IN (SELECT \"t1\".\"id\" FROM \"p\" AS \"t1\" WHERE ('\n             '\"t1\".\"i_id\" = ?)))', [i1.id]),\n            ('DELETE FROM \"ps\" WHERE ('\n             '\"ps\".\"s_id\" IN (SELECT \"t1\".\"id\" FROM \"s\" AS \"t1\" WHERE ('\n             '\"t1\".\"i_id\" = ?)))', [i1.id]),\n            ('DELETE FROM \"s\" WHERE (\"s\".\"i_id\" = ?)', [i1.id]),\n            ('DELETE FROM \"p\" WHERE (\"p\".\"i_id\" = ?)', [i1.id]),\n            ('DELETE FROM \"i\" WHERE (\"i\".\"id\" = ?)', [i1.id]),\n        ])\n\n        models = [I, S, P, PS, PP, O, OX]\n        counts = {OX: 2}\n        for m in models:\n            self.assertEqual(m.select().count(), counts.get(m, 1))\n\n\nclass IMC(TestModel):\n    a = IntegerField()\n    b = IntegerField(null=True)\n\nclass TestChunkedInsertMany(ModelTestCase):\n    requires = [IMC]\n\n    def test_chunked_insert_many(self):\n        data = [(i, i if i % 2 == 0 else None) for i in range(100)]\n        for chunk in chunked(data, 10):\n            IMC.insert_many(chunk).execute()\n\n        q = IMC.select(IMC.a, IMC.b).order_by(IMC.id).tuples()\n        self.assertEqual(list(q), data)\n        IMC.delete().execute()\n\n        data = [{'a': i, 'b': i if i % 2 == 0 else None} for i in range(100)]\n        for chunk in chunked(data, 5):\n            IMC.insert_many(chunk).execute()\n        q = IMC.select(IMC.a, IMC.b).order_by(IMC.id).dicts()\n        self.assertEqual(list(q), data)\n        IMC.delete().execute()\n\n\n@slow_test()\nclass TestThreadSafeMetaRegression(ModelTestCase):\n    def test_thread_safe_meta(self):\n        d1 = get_in_memory_db()\n        d2 = get_in_memory_db()\n\n        class Meta:\n            database = d1\n            model_metadata_class = ThreadSafeDatabaseMetadata\n        attrs = {'Meta': Meta}\n        for i in range(1, 30):\n            attrs['f%d' % i] = IntegerField()\n        M = type('M', (TestModel,), attrs)\n\n        sql = ('SELECT \"t1\".\"f1\", \"t1\".\"f2\", \"t1\".\"f3\", \"t1\".\"f4\" '\n               'FROM \"m\" AS \"t1\"')\n        query = M.select(M.f1, M.f2, M.f3, M.f4)\n\n        def swap_db():\n            for i in range(100):\n                self.assertEqual(M._meta.database, d1)\n                self.assertSQL(query, sql)\n                with d2.bind_ctx([M]):\n                    self.assertEqual(M._meta.database, d2)\n                    self.assertSQL(query, sql)\n                self.assertEqual(M._meta.database, d1)\n                self.assertSQL(query, sql)\n\n        # From a separate thread, swap the database and verify it works\n        # correctly.\n        threads = [threading.Thread(target=swap_db)\n                   for i in range(20)]\n        for t in threads: t.start()\n        for t in threads: t.join()\n\n        # In the main thread the original database has not been altered.\n        self.assertEqual(M._meta.database, d1)\n        self.assertSQL(query, sql)\n"
  },
  {
    "path": "tests/results.py",
    "content": "import datetime\n\nfrom peewee import *\n\nfrom .base import get_in_memory_db\nfrom .base import ModelTestCase\nfrom .base_models import *\n\n\ndef lange(x, y=None):\n    if y is None:\n        value = range(x)\n    else:\n        value = range(x, y)\n    return list(value)\n\n\nclass TestCursorWrapper(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User]\n\n    def test_iteration(self):\n        for i in range(10):\n            User.create(username=str(i))\n\n        query = User.select()\n        cursor = query.execute()\n\n        first_five = []\n        for i, u in enumerate(cursor):\n            first_five.append(int(u.username))\n            if i == 4: break\n\n        self.assertEqual(first_five, lange(5))\n        names = lambda i: [int(obj.username) for obj in i]\n        self.assertEqual(names(query[5:]), lange(5, 10))\n        self.assertEqual(names(query[2:5]), lange(2, 5))\n\n        for i in range(2):\n            self.assertEqual(names(cursor), lange(10))\n\n    def test_count(self):\n        for i in range(5): User.create(username=str(i))\n        with self.assertQueryCount(1):\n            query = User.select()\n            self.assertEqual(len(query), 5)\n\n            cursor = query.execute()\n            self.assertEqual(len(cursor), 5)\n\n        with self.assertQueryCount(1):\n            query = query.where(User.username != '0')\n            cursor = query.execute()\n            self.assertEqual(len(cursor), 4)\n            self.assertEqual(len(query), 4)\n\n    def test_nested_iteration(self):\n        for i in range(4): User.create(username=str(i))\n        with self.assertQueryCount(1):\n            query = User.select().order_by(User.username)\n            outer = []\n            inner = []\n            for o_user in query:\n                outer.append(int(o_user.username))\n                for i_user in query:\n                    inner.append(int(i_user.username))\n\n            self.assertEqual(outer, lange(4))\n            self.assertEqual(inner, lange(4) * 4)\n\n    def test_iterator_protocol(self):\n        for i in range(3): User.create(username=str(i))\n\n        with self.assertQueryCount(1):\n            query = User.select().order_by(User.id)\n            cursor = query.execute()\n            for _ in range(2):\n                for user in cursor: pass\n\n            it = iter(cursor)\n            for obj in it:\n                pass\n            self.assertRaises(StopIteration, next, it)\n            self.assertEqual([int(u.username) for u in cursor], lange(3))\n            self.assertEqual(query[0].username, '0')\n            self.assertEqual(query[2].username, '2')\n            self.assertRaises(StopIteration, next, it)\n\n    def test_iterator(self):\n        for i in range(3): User.create(username=str(i))\n\n        with self.assertQueryCount(1):\n            cursor = User.select().order_by(User.id).execute()\n            usernames = [int(u.username) for u in cursor.iterator()]\n            self.assertEqual(usernames, lange(3))\n\n        self.assertTrue(cursor.populated)\n        self.assertEqual(cursor.row_cache, [])\n\n        with self.assertQueryCount(0):\n            self.assertEqual(list(cursor), [])\n\n    def test_query_iterator(self):\n        for i in range(3): User.create(username=str(i))\n\n        with self.assertQueryCount(1):\n            query = User.select().order_by(User.id)\n            usernames = [int(u.username) for u in query.iterator()]\n            self.assertEqual(usernames, lange(3))\n\n        with self.assertQueryCount(0):\n            self.assertEqual(list(query), [])\n\n    def test_row_cache(self):\n        def assertCache(cursor, n):\n            self.assertEqual([int(u.username) for u in cursor.row_cache],\n                             lange(n))\n\n        for i in range(10): User.create(username=str(i))\n\n        with self.assertQueryCount(1):\n            cursor = User.select().order_by(User.id).execute()\n            cursor.fill_cache(5)\n            self.assertFalse(cursor.populated)\n            assertCache(cursor, 5)\n\n            cursor.fill_cache(5)\n            assertCache(cursor, 5)\n\n            cursor.fill_cache(6)\n            assertCache(cursor, 6)\n            self.assertFalse(cursor.populated)\n\n            cursor.fill_cache(11)\n            self.assertTrue(cursor.populated)\n            assertCache(cursor, 10)\n\n\nclass TestRowTypes(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User, Tweet]\n\n    def make_query(self, *exprs):\n        count = 0\n        accum = []\n        for expr in exprs:\n            if isinstance(expr, str):\n                accum.append(Value('v%d' % count).alias(expr))\n                count += 1\n            else:\n                accum.append(expr)\n        return User.select(*accum).order_by(User.username)\n\n    def test_namedtuples(self):\n        User.create(username='u1')\n\n        query = self.make_query(User.username).namedtuples()\n        self.assertEqual([u.username for u in query], ['u1'])\n\n        row = query[0]\n        self.assertEqual(repr(row), 'Row(username=\\'u1\\')')\n\n        query = (self\n                 .make_query(User.username, 'username', 'username')\n                 .namedtuples())\n        row, = list(query)\n        self.assertEqual(row, ('u1', 'v0', 'v1'))\n        self.assertEqual(row.username, 'u1')\n        self.assertEqual(row.username_2, 'v0')\n        self.assertEqual(row.username_3, 'v1')\n\n        query = (self\n                 .make_query('username', User.username)\n                 .namedtuples())\n        row, = list(query)\n        self.assertEqual(row, ('v0', 'u1'))\n        self.assertEqual(row.username, 'v0')\n        self.assertEqual(row.username_2, 'u1')\n\n        query = (self\n                 .make_query('\"foo\"', '\"t1\".\"foo\"()', 'foo ')\n                 .namedtuples())\n        row, = list(query)\n        self.assertEqual(row, ('v0', 'v1', 'v2'))\n        self.assertEqual(row.foo, 'v0')\n        self.assertEqual(row.foo_2, 'v1')\n        self.assertEqual(row.foo_3, 'v2')\n\n    def test_dicts(self):\n        User.create(username='u1')\n\n        query = self.make_query(User.username).dicts()\n        self.assertEqual(list(query), [{'username': 'u1'}])\n\n        query = (self\n                 .make_query(User.username, 'username', 'username')\n                 .dicts())\n        row, = list(query)\n        self.assertEqual(row, {\n            'username': 'u1',\n            'username_2': 'v0',\n            'username_3': 'v1'})\n\n        query = (self\n                 .make_query('username', User.username)\n                 .dicts())\n        row, = list(query)\n        self.assertEqual(row, {\n            'username': 'v0',\n            'username_2': 'u1'})\n\n        query = (self\n                 .make_query('\"foo\"', '\"t1\".\"foo\"()', 'foo ')\n                 .dicts())\n        row, = list(query)\n        self.assertEqual(row, {\n            '\"foo\"': 'v0',\n            '\"t1\".\"foo\"()': 'v1',\n            'foo ': 'v2'})\n\n    def test_dicts_flat(self):\n        u = User.create(username='u1')\n        for i in range(3):\n            Tweet.create(user=u, content='t%d' % (i + 1))\n\n        query = (Tweet\n                 .select(Tweet, User.username)\n                 .join(User)\n                 .order_by(Tweet.id)\n                 .dicts())\n        with self.assertQueryCount(1):\n            results = [(r['id'], r['content'], r['username']) for r in query]\n            self.assertEqual(results, [\n                (1, 't1', 'u1'),\n                (2, 't2', 'u1'),\n                (3, 't3', 'u1')])\n\n    def test_model_objects(self):\n        User.create(username='u1')\n\n        query = self.make_query(User.username).objects()\n        self.assertEqual([u.username for u in query], ['u1'])\n\n        query = (self\n                 .make_query(User.username, 'username', 'username')\n                 .objects())\n        row, = list(query)\n        self.assertEqual(row.username, 'u1')\n        self.assertEqual(row.username_2, 'v0')\n        self.assertEqual(row.username_3, 'v1')\n\n        query = (self\n                 .make_query('username', User.username)\n                 .objects())\n        row, = list(query)\n        self.assertEqual(row.username, 'v0')\n        self.assertEqual(row.username_2, 'u1')\n\n        query = (self\n                 .make_query('\"foo\"', '\"t1\".\"foo\"()', 'foo ')\n                 .objects())\n        row, = list(query)\n        self.assertEqual(row.foo, 'v0')\n        self.assertEqual(row.foo_2, 'v1')\n        self.assertEqual(row.foo_3, 'v2')\n\n    def test_model_objects_flat(self):\n        huey = User.create(username='huey')\n        mickey = User.create(username='mickey')\n        for user, tweet in ((huey, 'meow'), (huey, 'purr'), (mickey, 'woof')):\n            Tweet.create(user=user, content=tweet)\n\n        query = (Tweet\n                 .select(Tweet, User.username)\n                 .join(User)\n                 .order_by(Tweet.id)\n                 .objects())\n        with self.assertQueryCount(1):\n            self.assertEqual([(t.username, t.content) for t in query], [\n                ('huey', 'meow'),\n                ('huey', 'purr'),\n                ('mickey', 'woof')])\n\n    def test_models(self):\n        huey = User.create(username='huey')\n        mickey = User.create(username='mickey')\n        tids = []\n        for user, tweet in ((huey, 'meow'), (huey, 'purr'), (mickey, 'woof')):\n            tids.append(Tweet.create(user=user, content=tweet).id)\n\n        query = (Tweet\n                 .select(Tweet, User)\n                 .join(User)\n                 .order_by(Tweet.id))\n        with self.assertQueryCount(1):\n            accum = [(t.user.id, t.user.username, t.id, t.content)\n                     for t in query]\n            self.assertEqual(accum, [\n                (huey.id, 'huey', tids[0], 'meow'),\n                (huey.id, 'huey', tids[1], 'purr'),\n                (mickey.id, 'mickey', tids[2], 'woof')])\n\n\nclass Reg(TestModel):\n    key = TextField()\n    ts = DateTimeField()\n\nclass TestSpecifyConverter(ModelTestCase):\n    requires = [Reg]\n\n    def test_specify_converter(self):\n        D = lambda d: datetime.datetime(2020, 1, d)\n        for i in range(1, 4):\n            Reg.create(key='k%s' % i, ts=D(i))\n\n        RA = Reg.alias()\n        subq = RA.select(RA.key, RA.ts, RA.ts.alias('aliased'))\n\n        ra_a = subq.c.aliased.alias('aliased')\n        q = (Reg\n             .select(Reg.key, subq.c.ts.alias('ts'),\n                     ra_a.converter(Reg.ts.python_value))\n             .join(subq, on=(Reg.key == subq.c.key).alias('rsub'))\n             .order_by(Reg.key))\n        results = [(r.key, r.ts, r.aliased) for r in q.objects()]\n        self.assertEqual(results, [\n            ('k1', D(1), D(1)),\n            ('k2', D(2), D(2)),\n            ('k3', D(3), D(3))])\n\n        results2 = [(r.key, r.rsub.ts, r.rsub.aliased)\n                    for r in q]\n        self.assertEqual(results, [\n            ('k1', D(1), D(1)),\n            ('k2', D(2), D(2)),\n            ('k3', D(3), D(3))])\n"
  },
  {
    "path": "tests/returning.py",
    "content": "import unittest\n\nfrom peewee import *\nfrom peewee import __sqlite_version__\n\nfrom .base import db\nfrom .base import skip_unless\nfrom .base import IS_SQLITE\nfrom .base import ModelTestCase\nfrom .base import TestModel\n\n\nclass Reg(TestModel):\n    k = CharField()\n    v = IntegerField()\n    x = IntegerField()\n    class Meta:\n        indexes = (\n            (('k', 'v'), True),\n        )\n\n\nreturning_support = db.returning_clause or (IS_SQLITE and\n                                            __sqlite_version__ >= (3, 35, 0))\n\n\n@skip_unless(returning_support, 'database does not support RETURNING')\nclass TestReturningIntegration(ModelTestCase):\n    requires = [Reg]\n\n    def test_crud(self):\n        iq = Reg.insert_many([('k1', 1, 0), ('k2', 2, 0)]).returning(Reg)\n        self.assertEqual([(r.id is not None, r.k, r.v) for r in iq.execute()],\n                         [(True, 'k1', 1), (True, 'k2', 2)])\n\n        iq = (Reg\n              .insert_many([('k1', 1, 1), ('k2', 2, 1), ('k3', 3, 0)])\n              .on_conflict(\n                  conflict_target=[Reg.k, Reg.v],\n                  preserve=[Reg.x],\n                  update={Reg.v: Reg.v + 1},\n                  where=(Reg.k != 'k1'))\n              .returning(Reg))\n        ic = iq.execute()\n        self.assertEqual([(r.id is not None, r.k, r.v, r.x) for r in ic], [\n            (True, 'k2', 3, 1),\n            (True, 'k3', 3, 0)])\n\n        uq = (Reg\n              .update({Reg.v: Reg.v - 1, Reg.x: Reg.x + 1})\n              .where(Reg.k != 'k1')\n              .returning(Reg))\n        self.assertEqual([(r.k, r.v, r.x) for r in uq.execute()], [\n            ('k2', 2, 2), ('k3', 2, 1)])\n\n        dq = Reg.delete().where(Reg.k != 'k1').returning(Reg)\n        self.assertEqual([(r.k, r.v, r.x) for r in dq.execute()], [\n            ('k2', 2, 2), ('k3', 2, 1)])\n\n    def test_returning_expression(self):\n        Rs = (Reg.v + Reg.x).alias('s')\n        iq = (Reg\n              .insert_many([('k1', 1, 10), ('k2', 2, 20)])\n              .returning(Reg.k, Reg.v, Rs))\n        self.assertEqual([(r.k, r.v, r.s) for r in iq.execute()], [\n            ('k1', 1, 11), ('k2', 2, 22)])\n\n        uq = (Reg\n              .update({Reg.k: Reg.k + 'x', Reg.v: Reg.v + 1})\n              .returning(Reg.k, Reg.v, Rs))\n        self.assertEqual([(r.k, r.v, r.s) for r in uq.execute()], [\n            ('k1x', 2, 12), ('k2x', 3, 23)])\n\n        dq = Reg.delete().returning(Reg.k, Reg.v, Rs)\n        self.assertEqual([(r.k, r.v, r.s) for r in dq.execute()], [\n            ('k1x', 2, 12), ('k2x', 3, 23)])\n\n    def test_returning_types(self):\n        Rs = (Reg.v + Reg.x).alias('s')\n        mapping = (\n            ((lambda q: q), (lambda r: (r.k, r.v, r.s))),\n            ((lambda q: q.dicts()), (lambda r: (r['k'], r['v'], r['s']))),\n            ((lambda q: q.tuples()), (lambda r: r)),\n            ((lambda q: q.namedtuples()), (lambda r: (r.k, r.v, r.s))))\n\n        for qconv, r2t in mapping:\n            iq = (Reg\n                  .insert_many([('k1', 1, 10), ('k2', 2, 20)])\n                  .returning(Reg.k, Reg.v, Rs))\n            self.assertEqual([r2t(r) for r in qconv(iq).execute()], [\n                ('k1', 1, 11), ('k2', 2, 22)])\n\n            uq = (Reg\n                  .update({Reg.k: Reg.k + 'x', Reg.v: Reg.v + 1})\n                  .returning(Reg.k, Reg.v, Rs))\n            self.assertEqual([r2t(r) for r in qconv(uq).execute()], [\n                ('k1x', 2, 12), ('k2x', 3, 23)])\n\n            dq = Reg.delete().returning(Reg.k, Reg.v, Rs)\n            self.assertEqual([r2t(r) for r in qconv(dq).execute()], [\n                ('k1x', 2, 12), ('k2x', 3, 23)])\n"
  },
  {
    "path": "tests/schema.py",
    "content": "import datetime\n\nfrom peewee import *\nfrom peewee import NodeList\n\nfrom .base import BaseTestCase\nfrom .base import get_in_memory_db\nfrom .base import IS_CRDB\nfrom .base import IS_SQLITE\nfrom .base import ModelDatabaseTestCase\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base_models import Category\nfrom .base_models import Note\nfrom .base_models import Person\nfrom .base_models import Relationship\nfrom .base_models import User\n\n\nclass TMUnique(TestModel):\n    data = TextField(unique=True)\n\n\nclass TMSequence(TestModel):\n    value = IntegerField(sequence='test_seq')\n\n\nclass TMIndexes(TestModel):\n    alpha = IntegerField()\n    beta = IntegerField()\n    gamma = IntegerField()\n\n    class Meta:\n        indexes = (\n            (('alpha', 'beta'), True),\n            (('beta', 'gamma'), False))\n\n\nclass TMConstraints(TestModel):\n    data = IntegerField(null=True, constraints=[Check('data < 5')])\n    value = TextField(collation='NOCASE')\n    added = DateTimeField(constraints=[Default('CURRENT_TIMESTAMP')])\n\n\nclass TMNamedConstraints(TestModel):\n    fk = ForeignKeyField('self', null=True, constraint_name='tmc_fk')\n    k = TextField()\n    v = IntegerField(constraints=[Check('v in (1, 2)')])\n    class Meta:\n        constraints = [Check('k != \\'kx\\'', name='chk_k')]\n\n\nclass CacheData(TestModel):\n    key = TextField(unique=True)\n    value = TextField()\n\n    class Meta:\n        schema = 'cache'\n\n\nclass Article(TestModel):\n    name = TextField(unique=True)\n    timestamp = TimestampField()\n    status = IntegerField()\n    flags = IntegerField()\n\n\nArticle.add_index(Article.timestamp.desc(), Article.status)\n\nidx = (Article\n       .index(Article.name, Article.timestamp, Article.flags.bin_and(4))\n       .where(Article.status == 1))\nArticle.add_index(idx)\nArticle.add_index(SQL('CREATE INDEX \"article_foo\" ON \"article\" (\"flags\" & 3)'))\n\n\nclass TestModelDDL(ModelDatabaseTestCase):\n    database = get_in_memory_db()\n    requires = [Article, CacheData, Category, Note, Person, Relationship,\n                TMUnique, TMSequence, TMIndexes, TMConstraints,\n                TMNamedConstraints, User]\n\n    def test_database_required(self):\n        class MissingDB(Model):\n            data = TextField()\n\n        self.assertRaises(ImproperlyConfigured, MissingDB.create_table)\n\n    def assertCreateTable(self, model_class, expected):\n        sql, params = model_class._schema._create_table(False).query()\n        self.assertEqual(params, [])\n\n        indexes = []\n        for create_index in model_class._schema._create_indexes(False):\n            isql, params = create_index.query()\n            self.assertEqual(params, [])\n            indexes.append(isql)\n\n        self.assertEqual([sql] + indexes, expected)\n\n    def assertIndexes(self, model_class, expected):\n        indexes = []\n        for create_index in model_class._schema._create_indexes(False):\n            indexes.append(create_index.query())\n\n        self.assertEqual(indexes, expected)\n\n    def test_model_fk_schema(self):\n        class Base(TestModel):\n            class Meta:\n                database = self.database\n        class User(Base):\n            username = TextField()\n            class Meta:\n                schema = 'foo'\n        class Tweet(Base):\n            user = ForeignKeyField(User)\n            content = TextField()\n            class Meta:\n                schema = 'bar'\n\n        self.assertCreateTable(User, [\n            ('CREATE TABLE \"foo\".\"user\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"username\" TEXT NOT NULL)')])\n        self.assertCreateTable(Tweet, [\n            ('CREATE TABLE \"bar\".\"tweet\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"user_id\" INTEGER NOT NULL, \"content\" TEXT NOT NULL, '\n             'FOREIGN KEY (\"user_id\") REFERENCES \"foo\".\"user\" (\"id\"))'),\n            ('CREATE INDEX \"bar\".\"tweet_user_id\" ON \"tweet\" (\"user_id\")')])\n\n    def test_bigauto_and_fk(self):\n        class CustomDB(SqliteDatabase):\n            field_types = {\n                'BIGAUTO': 'BIGAUTO',\n                'BIGINT': 'BIGINT'}\n        db = CustomDB(None)\n\n        class User(db.Model):\n            id = BigAutoField()\n        class Tweet(db.Model):\n            user = ForeignKeyField(User)\n\n        self.assertCreateTable(User, [\n            ('CREATE TABLE \"user\" (\"id\" BIGAUTO NOT NULL PRIMARY KEY)')])\n        self.assertCreateTable(Tweet, [\n            ('CREATE TABLE \"tweet\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"user_id\" BIGINT NOT NULL, FOREIGN KEY (\"user_id\") REFERENCES '\n             '\"user\" (\"id\"))'),\n            ('CREATE INDEX \"tweet_user_id\" ON \"tweet\" (\"user_id\")')])\n\n    def test_model_indexes_with_schema(self):\n        # Attach cache database so we can reference \"cache.\" as the schema.\n        self.database.execute_sql(\"attach database ':memory:' as cache;\")\n        self.assertCreateTable(CacheData, [\n            ('CREATE TABLE \"cache\".\"cache_data\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, \"key\" TEXT NOT NULL, '\n             '\"value\" TEXT NOT NULL)'),\n            ('CREATE UNIQUE INDEX \"cache\".\"cache_data_key\" ON \"cache_data\" '\n             '(\"key\")')])\n\n        # Actually create the table to verify it works correctly.\n        CacheData.create_table()\n\n        # Introspect the database and get indexes for the \"cache\" schema.\n        indexes = self.database.get_indexes('cache_data', 'cache')\n        self.assertEqual(len(indexes), 1)\n        index_metadata = indexes[0]\n        self.assertEqual(index_metadata.name, 'cache_data_key')\n\n        # Verify the index does not exist in the main schema.\n        self.assertEqual(len(self.database.get_indexes('cache_data')), 0)\n\n        class TestDatabase(Database):\n            index_schema_prefix = False\n\n        # When \"index_schema_prefix == False\", the index name is not prefixed\n        # with the schema, and the schema is referenced via the table name.\n        with CacheData.bind_ctx(TestDatabase(None)):\n            self.assertCreateTable(CacheData, [\n                ('CREATE TABLE \"cache\".\"cache_data\" ('\n                 '\"id\" INTEGER NOT NULL PRIMARY KEY, \"key\" TEXT NOT NULL, '\n                 '\"value\" TEXT NOT NULL)'),\n                ('CREATE UNIQUE INDEX \"cache_data_key\" ON \"cache\".\"cache_data\"'\n                 ' (\"key\")')])\n\n    def test_model_indexes(self):\n        self.assertIndexes(Article, [\n            ('CREATE UNIQUE INDEX \"article_name\" ON \"article\" (\"name\")', []),\n            ('CREATE INDEX \"article_timestamp_status\" ON \"article\" ('\n             '\"timestamp\" DESC, \"status\")', []),\n            ('CREATE INDEX \"article_name_timestamp\" ON \"article\" ('\n             '\"name\", \"timestamp\", (\"flags\" & 4)) '\n             'WHERE (\"status\" = 1)', []),\n            ('CREATE INDEX \"article_foo\" ON \"article\" (\"flags\" & 3)', []),\n        ])\n\n    def test_model_index_types(self):\n        class Event(TestModel):\n            key = TextField()\n            timestamp = TimestampField(index=True, index_type='BRIN')\n            class Meta:\n                database = self.database\n\n        self.assertIndexes(Event, [\n            ('CREATE INDEX \"event_timestamp\" ON \"event\" '\n             'USING BRIN (\"timestamp\")', [])])\n\n        # Check that we support MySQL-style USING clause.\n        idx, = Event._meta.fields_to_index()\n        self.assertSQL(idx, (\n            'CREATE INDEX IF NOT EXISTS \"event_timestamp\" '\n            'USING BRIN ON \"event\" (\"timestamp\")'), [],\n            index_using_precedes_table=True)\n\n    def test_model_indexes_custom_tablename(self):\n        class KV(TestModel):\n            key = TextField()\n            value = TextField()\n            timestamp = TimestampField(index=True)\n            class Meta:\n                database = self.database\n                indexes = (\n                    (('key', 'value'), True),\n                )\n                table_name = 'kvs'\n\n        self.assertIndexes(KV, [\n            ('CREATE INDEX \"kvs_timestamp\" ON \"kvs\" (\"timestamp\")', []),\n            ('CREATE UNIQUE INDEX \"kvs_key_value\" ON \"kvs\" (\"key\", \"value\")',\n             [])])\n\n    def test_model_indexes_computed_columns(self):\n        class FuncIdx(TestModel):\n            a = IntegerField()\n            b = IntegerField()\n            class Meta:\n                database = self.database\n\n        i = FuncIdx.index(FuncIdx.a, FuncIdx.b, fn.SUM(FuncIdx.a + FuncIdx.b))\n        FuncIdx.add_index(i)\n\n        self.assertIndexes(FuncIdx, [\n            ('CREATE INDEX \"func_idx_a_b\" ON \"func_idx\" '\n             '(\"a\", \"b\", SUM(\"a\" + \"b\"))', []),\n        ])\n\n    def test_model_indexes_complex_columns(self):\n        class Taxonomy(TestModel):\n            name = CharField()\n            name_class = CharField()\n            class Meta:\n                database = self.database\n\n        name = NodeList((fn.LOWER(Taxonomy.name), SQL('varchar_pattern_ops')))\n        index = (Taxonomy\n                 .index(name, Taxonomy.name_class)\n                 .where(Taxonomy.name_class == 'scientific name'))\n        Taxonomy.add_index(index)\n\n        self.assertIndexes(Taxonomy, [\n            ('CREATE INDEX \"taxonomy_name_class\" ON \"taxonomy\" ('\n             'LOWER(\"name\") varchar_pattern_ops, \"name_class\") '\n             'WHERE (\"name_class\" = ?)', ['scientific name']),\n        ])\n\n    def test_legacy_model_table_and_indexes(self):\n        class Base(Model):\n            class Meta:\n                database = self.database\n\n        class WebHTTPRequest(Base):\n            timestamp = DateTimeField(index=True)\n            data = TextField()\n\n        self.assertTrue(WebHTTPRequest._meta.legacy_table_names)\n        self.assertCreateTable(WebHTTPRequest, [\n            ('CREATE TABLE \"webhttprequest\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"timestamp\" DATETIME NOT NULL, \"data\" TEXT NOT NULL)'),\n            ('CREATE INDEX \"webhttprequest_timestamp\" ON \"webhttprequest\" '\n             '(\"timestamp\")')])\n\n        # Table name is explicit, but legacy table names == false, so we get\n        # the new index name format.\n        class FooBar(Base):\n            data = IntegerField(unique=True)\n            class Meta:\n                legacy_table_names = False\n                table_name = 'foobar_tbl'\n\n        self.assertFalse(FooBar._meta.legacy_table_names)\n        self.assertCreateTable(FooBar, [\n            ('CREATE TABLE \"foobar_tbl\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"data\" INTEGER NOT NULL)'),\n            ('CREATE UNIQUE INDEX \"foobar_tbl_data\" ON \"foobar_tbl\" (\"data\")'),\n        ])\n\n        # Table name is explicit and legacy table names == true, so we get\n        # the old index name format.\n        class FooBar2(Base):\n            data = IntegerField(unique=True)\n            class Meta:\n                table_name = 'foobar2_tbl'\n\n        self.assertTrue(FooBar2._meta.legacy_table_names)\n        self.assertCreateTable(FooBar2, [\n            ('CREATE TABLE \"foobar2_tbl\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"data\" INTEGER NOT NULL)'),\n            ('CREATE UNIQUE INDEX \"foobar2_data\" ON \"foobar2_tbl\" (\"data\")')])\n\n    def test_without_pk(self):\n        class NoPK(TestModel):\n            data = TextField()\n            class Meta:\n                database = self.database\n                primary_key = False\n        self.assertCreateTable(NoPK, [\n            ('CREATE TABLE \"no_pk\" (\"data\" TEXT NOT NULL)')])\n\n    def test_without_rowid(self):\n        class NoRowid(TestModel):\n            key = TextField(primary_key=True)\n            value = TextField()\n\n            class Meta:\n                database = self.database\n                without_rowid = True\n\n        self.assertCreateTable(NoRowid, [\n            ('CREATE TABLE \"no_rowid\" ('\n             '\"key\" TEXT NOT NULL PRIMARY KEY, '\n             '\"value\" TEXT NOT NULL) WITHOUT ROWID')])\n\n        # Subclasses do not inherit \"without_rowid\" setting.\n        class SubNoRowid(NoRowid): pass\n\n        self.assertCreateTable(SubNoRowid, [\n            ('CREATE TABLE \"sub_no_rowid\" ('\n             '\"key\" TEXT NOT NULL PRIMARY KEY, '\n             '\"value\" TEXT NOT NULL)')])\n\n    def test_strict_tables(self):\n        class Strict(TestModel):\n            key = TextField(primary_key=True)\n            value = TextField()\n\n            class Meta:\n                database = self.database\n                strict_tables = True\n\n        self.assertCreateTable(Strict, [\n            ('CREATE TABLE \"strict\" ('\n             '\"key\" TEXT NOT NULL PRIMARY KEY, '\n             '\"value\" TEXT NOT NULL) STRICT')])\n\n        # Subclasses *do* inherit \"strict_tables\" setting.\n        class SubStrict(Strict): pass\n\n        self.assertCreateTable(SubStrict, [\n            ('CREATE TABLE \"sub_strict\" ('\n             '\"key\" TEXT NOT NULL PRIMARY KEY, '\n             '\"value\" TEXT NOT NULL) STRICT')])\n\n    def test_without_rowid_strict(self):\n        class KV(TestModel):\n            key = TextField(primary_key=True)\n            class Meta:\n                database = self.database\n                strict_tables = True\n                without_rowid = True\n\n        self.assertCreateTable(KV, [\n            ('CREATE TABLE \"kv\" (\"key\" TEXT NOT NULL PRIMARY KEY) '\n             'STRICT, WITHOUT ROWID')])\n\n        class SKV(KV):\n            pass\n\n        self.assertCreateTable(SKV, [\n            ('CREATE TABLE \"skv\" (\"key\" TEXT NOT NULL PRIMARY KEY) STRICT')])\n\n    def test_table_name(self):\n        class A(TestModel):\n            class Meta:\n                database = self.database\n                table_name = 'A_tbl'\n        class B(TestModel):\n            a = ForeignKeyField(A, backref='bs')\n            class Meta:\n                database = self.database\n                table_name = 'B_tbl'\n        self.assertCreateTable(A, [\n            'CREATE TABLE \"A_tbl\" (\"id\" INTEGER NOT NULL PRIMARY KEY)'])\n        self.assertCreateTable(B, [\n            ('CREATE TABLE \"B_tbl\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"a_id\" INTEGER NOT NULL, '\n             'FOREIGN KEY (\"a_id\") REFERENCES \"A_tbl\" (\"id\"))'),\n            'CREATE INDEX \"B_tbl_a_id\" ON \"B_tbl\" (\"a_id\")'])\n\n    def test_temporary_table(self):\n        sql, params = User._schema._create_table(temporary=True).query()\n        self.assertEqual(sql, (\n            'CREATE TEMPORARY TABLE IF NOT EXISTS \"users\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"username\" VARCHAR(255) NOT NULL)'))\n\n    def test_model_temporary_table(self):\n        class TempUser(User):\n            class Meta:\n                temporary = True\n\n        self.reset_sql_history()\n        TempUser.create_table()\n        TempUser.drop_table()\n        queries = [x.msg for x in self.history]\n        self.assertEqual(queries, [\n            ('CREATE TEMPORARY TABLE IF NOT EXISTS \"temp_user\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"username\" VARCHAR(255) NOT NULL)', []),\n            ('DROP TABLE IF EXISTS \"temp_user\"', [])])\n\n    def test_drop_table(self):\n        sql, params = User._schema._drop_table().query()\n        self.assertEqual(sql, 'DROP TABLE IF EXISTS \"users\"')\n\n        sql, params = User._schema._drop_table(cascade=True).query()\n        self.assertEqual(sql, 'DROP TABLE IF EXISTS \"users\" CASCADE')\n\n        sql, params = User._schema._drop_table(restrict=True).query()\n        self.assertEqual(sql, 'DROP TABLE IF EXISTS \"users\" RESTRICT')\n\n    def test_table_constraints(self):\n        class UKV(TestModel):\n            key = TextField()\n            value = TextField()\n            status = IntegerField()\n            class Meta:\n                constraints = [\n                    SQL('CONSTRAINT ukv_kv_uniq UNIQUE (key, value)'),\n                    Check('status > 0')]\n                database = self.database\n                table_name = 'ukv'\n\n        self.assertCreateTable(UKV, [\n            ('CREATE TABLE \"ukv\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"key\" TEXT NOT NULL, '\n             '\"value\" TEXT NOT NULL, '\n             '\"status\" INTEGER NOT NULL, '\n             'CONSTRAINT ukv_kv_uniq UNIQUE (key, value), '\n             'CHECK (status > 0))')])\n\n    def test_table_settings(self):\n        class KVSettings(TestModel):\n            key = TextField(primary_key=True)\n            value = TextField()\n            timestamp = TimestampField()\n            class Meta:\n                database = self.database\n                table_settings = ('PARTITION BY RANGE (timestamp)',\n                                  'WITHOUT ROWID')\n        self.assertCreateTable(KVSettings, [\n            ('CREATE TABLE \"kv_settings\" ('\n             '\"key\" TEXT NOT NULL PRIMARY KEY, '\n             '\"value\" TEXT NOT NULL, '\n             '\"timestamp\" INTEGER NOT NULL) '\n             'PARTITION BY RANGE (timestamp) '\n             'WITHOUT ROWID')])\n\n    def test_table_options(self):\n        class TOpts(TestModel):\n            key = TextField()\n            class Meta:\n                database = self.database\n                options = {\n                    'CHECKSUM': 1,\n                    'COMPRESSION': 'lz4'}\n\n        self.assertCreateTable(TOpts, [\n            ('CREATE TABLE \"t_opts\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"key\" TEXT NOT NULL, '\n             'CHECKSUM=1, COMPRESSION=lz4)')])\n\n    def test_table_and_index_creation(self):\n        self.assertCreateTable(Person, [\n            ('CREATE TABLE \"person\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"first\" VARCHAR(255) NOT NULL, '\n             '\"last\" VARCHAR(255) NOT NULL, '\n             '\"dob\" DATE NOT NULL)'),\n            'CREATE INDEX \"person_dob\" ON \"person\" (\"dob\")',\n            ('CREATE UNIQUE INDEX \"person_first_last\" ON '\n             '\"person\" (\"first\", \"last\")')])\n\n        self.assertCreateTable(Note, [\n            ('CREATE TABLE \"note\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"author_id\" INTEGER NOT NULL, '\n             '\"content\" TEXT NOT NULL, '\n             'FOREIGN KEY (\"author_id\") REFERENCES \"person\" (\"id\"))'),\n            'CREATE INDEX \"note_author_id\" ON \"note\" (\"author_id\")'])\n\n        self.assertCreateTable(Category, [\n            ('CREATE TABLE \"category\" ('\n             '\"name\" VARCHAR(20) NOT NULL PRIMARY KEY, '\n             '\"parent_id\" VARCHAR(20), '\n             'FOREIGN KEY (\"parent_id\") REFERENCES \"category\" (\"name\"))'),\n            'CREATE INDEX \"category_parent_id\" ON \"category\" (\"parent_id\")'])\n\n        self.assertCreateTable(Relationship, [\n            ('CREATE TABLE \"relationship\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"from_person_id\" INTEGER NOT NULL, '\n             '\"to_person_id\" INTEGER NOT NULL, '\n             'FOREIGN KEY (\"from_person_id\") REFERENCES \"person\" (\"id\"), '\n             'FOREIGN KEY (\"to_person_id\") REFERENCES \"person\" (\"id\"))'),\n            ('CREATE INDEX \"relationship_from_person_id\" '\n             'ON \"relationship\" (\"from_person_id\")'),\n            ('CREATE INDEX \"relationship_to_person_id\" '\n             'ON \"relationship\" (\"to_person_id\")')])\n\n        self.assertCreateTable(TMUnique, [\n            ('CREATE TABLE \"tm_unique\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"data\" TEXT NOT NULL)'),\n            'CREATE UNIQUE INDEX \"tm_unique_data\" ON \"tm_unique\" (\"data\")'])\n\n        self.assertCreateTable(TMSequence, [\n            ('CREATE TABLE \"tm_sequence\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"value\" INTEGER NOT NULL DEFAULT NEXTVAL(\\'test_seq\\'))')])\n\n        self.assertCreateTable(TMIndexes, [\n            ('CREATE TABLE \"tm_indexes\" (\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"alpha\" INTEGER NOT NULL, \"beta\" INTEGER NOT NULL, '\n             '\"gamma\" INTEGER NOT NULL)'),\n            ('CREATE UNIQUE INDEX \"tm_indexes_alpha_beta\" '\n             'ON \"tm_indexes\" (\"alpha\", \"beta\")'),\n            ('CREATE INDEX \"tm_indexes_beta_gamma\" '\n             'ON \"tm_indexes\" (\"beta\", \"gamma\")')])\n\n        self.assertCreateTable(TMConstraints, [\n            ('CREATE TABLE \"tm_constraints\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"data\" INTEGER CHECK (data < 5), '\n             '\"value\" TEXT NOT NULL COLLATE NOCASE, '\n             '\"added\" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP)')])\n\n        self.assertCreateTable(TMNamedConstraints, [\n            ('CREATE TABLE \"tm_named_constraints\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"fk_id\" INTEGER, '\n             '\"k\" TEXT NOT NULL, '\n             '\"v\" INTEGER NOT NULL '\n             'CHECK (v in (1, 2)), '\n             'CONSTRAINT \"tmc_fk\" FOREIGN KEY (\"fk_id\") '\n             'REFERENCES \"tm_named_constraints\" (\"id\"), '\n             'CONSTRAINT \"chk_k\" CHECK (k != \\'kx\\'))'),\n            ('CREATE INDEX \"tm_named_constraints_fk_id\" '\n             'ON \"tm_named_constraints\" (\"fk_id\")')])\n\n        sql, params = (TMNamedConstraints\n                       ._schema\n                       ._create_foreign_key(TMNamedConstraints.fk)\n                       .query())\n        self.assertEqual(sql, (\n            'ALTER TABLE \"tm_named_constraints\" ADD CONSTRAINT \"tmc_fk\" '\n            'FOREIGN KEY (\"fk_id\") REFERENCES \"tm_named_constraints\" (\"id\")'))\n\n    def test_index_name_truncation(self):\n        class LongIndex(TestModel):\n            a123456789012345678901234567890 = CharField()\n            b123456789012345678901234567890 = CharField()\n            c123456789012345678901234567890 = CharField()\n            class Meta:\n                database = self.database\n\n        fields = LongIndex._meta.sorted_fields[1:]\n        self.assertEqual(len(fields), 3)\n\n        idx = ModelIndex(LongIndex, fields)\n        ctx = LongIndex._schema._create_index(idx)\n        self.assertSQL(ctx, (\n            'CREATE INDEX IF NOT EXISTS \"'\n            'long_index_a123456789012345678901234567890_b123456789012_9dd2139'\n            '\" ON \"long_index\" ('\n            '\"a123456789012345678901234567890\", '\n            '\"b123456789012345678901234567890\", '\n            '\"c123456789012345678901234567890\")'), [])\n\n    def test_fk_non_pk_ddl(self):\n        class A(Model):\n            cf = CharField(max_length=100, unique=True)\n            df = DecimalField(\n                max_digits=4,\n                decimal_places=2,\n                auto_round=True,\n                unique=True)\n            class Meta:\n                database = self.database\n\n        class CF(TestModel):\n            a = ForeignKeyField(A, field='cf')\n            class Meta:\n                database = self.database\n\n        class DF(TestModel):\n            a = ForeignKeyField(A, field='df')\n            class Meta:\n                database = self.database\n\n        sql, params = CF._schema._create_table(safe=False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"cf\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"a_id\" VARCHAR(100) NOT NULL, '\n            'FOREIGN KEY (\"a_id\") REFERENCES \"a\" (\"cf\"))'))\n\n        sql, params = DF._schema._create_table(safe=False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"df\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"a_id\" DECIMAL(4, 2) NOT NULL, '\n            'FOREIGN KEY (\"a_id\") REFERENCES \"a\" (\"df\"))'))\n\n    def test_deferred_foreign_key(self):\n        class Language(TestModel):\n            name = CharField()\n            selected_snippet = DeferredForeignKey('Snippet', null=True)\n            class Meta:\n                database = self.database\n\n        class Snippet(TestModel):\n            code = TextField()\n            language = ForeignKeyField(Language, backref='snippets')\n            class Meta:\n                database = self.database\n\n        self.assertEqual(Snippet._meta.fields['language'].rel_model, Language)\n        self.assertEqual(Language._meta.fields['selected_snippet'].rel_model,\n                         Snippet)\n\n        sql, params = Snippet._schema._create_table(safe=False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"snippet\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"code\" TEXT NOT NULL, '\n            '\"language_id\" INTEGER NOT NULL, '\n            'FOREIGN KEY (\"language_id\") REFERENCES \"language\" (\"id\"))'))\n\n        sql, params = Language._schema._create_table(safe=False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"language\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"name\" VARCHAR(255) NOT NULL, '\n            '\"selected_snippet_id\" INTEGER)'))\n\n        sql, params = (Language\n                       ._schema\n                       ._create_foreign_key(Language.selected_snippet)\n                       .query())\n        self.assertEqual(sql, (\n            'ALTER TABLE \"language\" ADD CONSTRAINT '\n            '\"fk_language_selected_snippet_id_refs_snippet\" '\n            'FOREIGN KEY (\"selected_snippet_id\") REFERENCES \"snippet\" (\"id\")'))\n\n        class SnippetComment(TestModel):\n            snippet_long_foreign_key_identifier = ForeignKeyField(Snippet)\n            comment = TextField()\n            class Meta:\n                database = self.database\n\n        sql, params = SnippetComment._schema._create_table(safe=True).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE IF NOT EXISTS \"snippet_comment\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"snippet_long_foreign_key_identifier_id\" INTEGER NOT NULL, '\n            '\"comment\" TEXT NOT NULL, '\n            'FOREIGN KEY (\"snippet_long_foreign_key_identifier_id\") '\n            'REFERENCES \"snippet\" (\"id\"))'))\n\n        sql, params = (SnippetComment._schema\n                       ._create_foreign_key(\n                           SnippetComment.snippet_long_foreign_key_identifier)\n                       .query())\n        self.assertEqual(sql, (\n            'ALTER TABLE \"snippet_comment\" ADD CONSTRAINT \"'\n            'fk_snippet_comment_snippet_long_foreign_key_identifier_i_2a8b87d\"'\n            ' FOREIGN KEY (\"snippet_long_foreign_key_identifier_id\") '\n            'REFERENCES \"snippet\" (\"id\")'))\n\n    def test_deferred_foreign_key_inheritance(self):\n        class Base(TestModel):\n            class Meta:\n                database = self.database\n        class WithTimestamp(Base):\n            timestamp = TimestampField()\n        class Tweet(Base):\n            user = DeferredForeignKey('DUser')\n            content = TextField()\n        class TimestampTweet(Tweet, WithTimestamp): pass\n        class DUser(Base):\n            username = TextField()\n\n        sql, params = Tweet._schema._create_table(safe=False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"tweet\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"content\" TEXT NOT NULL, '\n            '\"user_id\" INTEGER NOT NULL)'))\n\n        sql, params = TimestampTweet._schema._create_table(safe=False).query()\n        self.assertEqual(sql, (\n            'CREATE TABLE \"timestamp_tweet\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"timestamp\" INTEGER NOT NULL, '\n            '\"content\" TEXT NOT NULL, '\n            '\"user_id\" INTEGER NOT NULL)'))\n\n    def test_identity_field(self):\n        class PG10Identity(TestModel):\n            id = IdentityField()\n            data = TextField()\n            class Meta:\n                database = self.database\n\n        self.assertCreateTable(PG10Identity, [\n            ('CREATE TABLE \"pg10_identity\" ('\n             '\"id\" INT GENERATED BY DEFAULT AS IDENTITY NOT NULL PRIMARY KEY, '\n             '\"data\" TEXT NOT NULL)'),\n        ])\n\n    def test_self_fk_inheritance(self):\n        class BaseCategory(TestModel):\n            parent = ForeignKeyField('self', backref='children')\n            class Meta:\n                database = self.database\n        class CatA1(BaseCategory):\n            name_a1 = TextField()\n        class CatA2(CatA1):\n            name_a2 = TextField()\n\n        self.assertTrue(CatA1.parent.rel_model is CatA1)\n        self.assertTrue(CatA2.parent.rel_model is CatA2)\n\n        self.assertCreateTable(CatA1, [\n            ('CREATE TABLE \"cat_a1\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"parent_id\" INTEGER NOT NULL, '\n             '\"name_a1\" TEXT NOT NULL, '\n             'FOREIGN KEY (\"parent_id\") REFERENCES \"cat_a1\" (\"id\"))'),\n            ('CREATE INDEX \"cat_a1_parent_id\" ON \"cat_a1\" (\"parent_id\")')])\n\n        self.assertCreateTable(CatA2, [\n            ('CREATE TABLE \"cat_a2\" ('\n             '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n             '\"parent_id\" INTEGER NOT NULL, '\n             '\"name_a1\" TEXT NOT NULL, '\n             '\"name_a2\" TEXT NOT NULL, '\n             'FOREIGN KEY (\"parent_id\") REFERENCES \"cat_a2\" (\"id\"))'),\n            ('CREATE INDEX \"cat_a2_parent_id\" ON \"cat_a2\" (\"parent_id\")')])\n\n    def test_field_ddl(self):\n        class Base(self.database.Model):\n            pass\n        class FC(Base):\n            code = FixedCharField(max_length=5)\n            name = CharField()\n\n        class Dbl(Base):\n            value = DoubleField()\n            label = CharField()\n\n        class SmInt(Base):\n            value = SmallIntegerField()\n            label = CharField()\n\n        self.assertSQL(FC._schema._create_table(False), (\n            'CREATE TABLE \"fc\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"code\" CHAR(5) NOT NULL, '\n            '\"name\" VARCHAR(255) NOT NULL)'), [])\n\n        self.assertSQL(Dbl._schema._create_table(False), (\n            'CREATE TABLE \"dbl\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"value\" REAL NOT NULL, '\n            '\"label\" VARCHAR(255) NOT NULL)'), [])\n\n        self.assertSQL(SmInt._schema._create_table(False), (\n            'CREATE TABLE \"smint\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"value\" INTEGER NOT NULL, '\n            '\"label\" VARCHAR(255) NOT NULL)'), [])\n\n\nclass NoteX(TestModel):\n    content = TextField()\n    timestamp = TimestampField()\n    status = IntegerField()\n    flags = IntegerField()\n\n\nclass TestCreateAs(ModelTestCase):\n    requires = [NoteX]\n    test_data = (\n        # name, timestamp, status, flags.\n        (1, 'n1', datetime.datetime(2019, 1, 1), 1, 1),\n        (2, 'n2', datetime.datetime(2019, 1, 2), 2, 1),\n        (3, 'n3', datetime.datetime(2019, 1, 3), 9, 1),\n        (4, 'nx', datetime.datetime(2019, 1, 1), 9, 0))\n\n    def setUp(self):\n        super(TestCreateAs, self).setUp()\n        fields = NoteX._meta.sorted_fields\n        NoteX.insert_many(self.test_data, fields=fields).execute()\n\n    def tearDown(self):\n        class Note2(TestModel):\n            class Meta:\n                database = self.database\n        self.database.drop_tables([Note2])\n        super(TestCreateAs, self).tearDown()\n\n    def test_create_as(self):\n        status = Case(NoteX.status, (\n            (1, 'published'),\n            (2, 'draft'),\n            (9, 'deleted')))\n\n        query = (NoteX\n                 .select(NoteX.id, NoteX.content, NoteX.timestamp,\n                         status.alias('status'))\n                 .where(NoteX.flags == SQL('1')))\n        query.create_table('note2')\n\n        class Note2(TestModel):\n            id = IntegerField()\n            content = TextField()\n            timestamp = TimestampField()\n            status = TextField()\n            class Meta:\n                database = self.database\n\n        query = Note2.select().order_by(Note2.id)\n        self.assertEqual(list(query.tuples()), [\n            (1, 'n1', datetime.datetime(2019, 1, 1), 'published'),\n            (2, 'n2', datetime.datetime(2019, 1, 2), 'draft'),\n            (3, 'n3', datetime.datetime(2019, 1, 3), 'deleted')])\n\n\nclass TestModelSetTableName(BaseTestCase):\n    def test_set_table_name(self):\n        class Foo(TestModel):\n            pass\n\n        self.assertEqual(Foo._meta.table_name, 'foo')\n        self.assertEqual(Foo._meta.table.__name__, 'foo')\n\n        # Writing the attribute directly does not update the cached Table name.\n        Foo._meta.table_name = 'foo2'\n        self.assertEqual(Foo._meta.table.__name__, 'foo')\n\n        # Use the helper-method.\n        Foo._meta.set_table_name('foo3')\n        self.assertEqual(Foo._meta.table.__name__, 'foo3')\n\n\nclass TestTruncateTable(ModelTestCase):\n    requires = [User]\n\n    def test_truncate_table(self):\n        for i in range(3):\n            User.create(username='u%s' % i)\n\n        ctx = User._schema._truncate_table()\n        if IS_SQLITE:\n            self.assertSQL(ctx, 'DELETE FROM \"users\"', [])\n        else:\n            sql, _ = ctx.query()\n            self.assertTrue(sql.startswith('TRUNCATE TABLE '))\n\n        User.truncate_table()\n        self.assertEqual(User.select().count(), 0)\n\n\nclass TestNamedConstraintsIntegration(ModelTestCase):\n    requires = [TMNamedConstraints]\n\n    def setUp(self):\n        super(TestNamedConstraintsIntegration, self).setUp()\n        if IS_SQLITE:\n            self.database.pragma('foreign_keys', 'on')\n\n    def test_named_constraints_integration(self):\n        t = TMNamedConstraints.create(k='k1', v=1)  # Sanity test.\n        fails = [\n            {'fk': t.id - 1, 'k': 'k2', 'v': 1},  # Invalid fk.\n            {'fk': t.id, 'k': 'k3', 'v': 0},  # Invalid val.\n            {'fk': t.id, 'k': 'kx', 'v': 1}]  # Invalid key.\n        for f in fails:\n            # MySQL may use OperationalError.\n            with self.assertRaises((IntegrityError, OperationalError)):\n                with self.database.atomic() as tx:\n                    TMNamedConstraints.create(**f)\n\n        self.assertEqual(len(TMNamedConstraints), 1)\n\n\nclass TMKV(TestModel):\n    key = CharField()\n    value = IntegerField()\n    extra = IntegerField()\n\nclass TMKVNew(TestModel):\n    key = CharField()\n    val = IntegerField()\n    class Meta:\n        primary_key = False\n        table_name = 'tmkv_new'\n\n\nclass TestCreateTableAsSQL(ModelDatabaseTestCase):\n    database = get_in_memory_db()\n    requires = [TMKV]\n\n    def test_create_table_as_sql(self):\n        query = (TMKV\n                 .select(TMKV.key, TMKV.value.alias('val'))\n                 .where(TMKV.extra < 4))\n        ctx = TMKV._schema._create_table_as('tmkv_new', query)\n        self.assertSQL(ctx, (\n            'CREATE TABLE IF NOT EXISTS \"tmkv_new\" AS '\n            'SELECT \"t1\".\"key\", \"t1\".\"value\" AS \"val\" FROM \"tmkv\" AS \"t1\" '\n            'WHERE (\"t1\".\"extra\" < ?)'), [4])\n\n        ctx = TMKV._schema._create_table_as(('alt', 'tmkv_new'), query)\n        self.assertSQL(ctx, (\n            'CREATE TABLE IF NOT EXISTS \"alt\".\"tmkv_new\" AS '\n            'SELECT \"t1\".\"key\", \"t1\".\"value\" AS \"val\" FROM \"tmkv\" AS \"t1\" '\n            'WHERE (\"t1\".\"extra\" < ?)'), [4])\n\n\nclass TestCreateTableAs(ModelTestCase):\n    requires = [TMKV]\n\n    def tearDown(self):\n        try:\n            TMKVNew.drop_table(safe=True)\n        except:\n            pass\n        super(TestCreateTableAs, self).tearDown()\n\n    def test_create_table_as(self):\n        TMKV.insert_many([('k%02d' % i, i, i) for i in range(10)]).execute()\n\n        query = (TMKV\n                 .select(TMKV.key, TMKV.value.alias('val'))\n                 .where(TMKV.extra < 4))\n        query.create_table('tmkv_new', safe=True)\n\n        expected = ['key', 'val']\n        if IS_CRDB: expected.append('rowid')  # CRDB adds this.\n\n        self.assertEqual(\n            [col.name for col in self.database.get_columns('tmkv_new')],\n            expected)\n\n        query = TMKVNew.select().order_by(TMKVNew.key)\n        self.assertEqual([(r.key, r.val) for r in query],\n                         [('k00', 0), ('k01', 1), ('k02', 2), ('k03', 3)])\n"
  },
  {
    "path": "tests/shortcuts.py",
    "content": "import operator\n\nfrom peewee import *\nfrom playhouse.shortcuts import *\n\nfrom .base import BaseTestCase\nfrom .base import DatabaseTestCase\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import db_loader\nfrom .base import get_in_memory_db\nfrom .base import requires_models\nfrom .base import requires_mysql\nfrom .base_models import Category\n\n\nclass User(TestModel):\n    username = TextField()\n\n    @property\n    def name_hash(self):\n        return sum(map(ord, self.username)) % 10\n\nclass Tweet(TestModel):\n    user = ForeignKeyField(User, backref='tweets')\n    content = TextField()\n\nclass Tag(TestModel):\n    tag = TextField()\n\nclass TweetTag(TestModel):\n    tweet = ForeignKeyField(Tweet)\n    tag = ForeignKeyField(Tag)\n\n    class Meta:\n        primary_key = CompositeKey('tweet', 'tag')\n\nclass Owner(TestModel):\n    name = TextField()\n\nclass Label(TestModel):\n    label = TextField()\n\nclass Gallery(TestModel):\n    name = TextField()\n    labels = ManyToManyField(Label, backref='galleries')\n    owner = ForeignKeyField(Owner, backref='galleries')\n\nGalleryLabel = Gallery.labels.through_model\n\nclass Student(TestModel):\n    name = TextField()\n\nStudentCourseProxy = DeferredThroughModel()\n\nclass Course(TestModel):\n    name = TextField()\n    students = ManyToManyField(Student, through_model=StudentCourseProxy,\n                               backref='courses')\n\nclass StudentCourse(TestModel):\n    student = ForeignKeyField(Student)\n    course = ForeignKeyField(Course)\n\nStudentCourseProxy.set_model(StudentCourse)\n\nclass Host(TestModel):\n    name = TextField()\n\nclass Service(TestModel):\n    host = ForeignKeyField(Host, backref='services')\n    name = TextField()\n\nclass Device(TestModel):\n    host = ForeignKeyField(Host, backref='+')\n    name = TextField()\n\nclass Basket(TestModel):\n    id = IntegerField(primary_key=True)\n\nclass Item(TestModel):\n    id = IntegerField(primary_key=True)\n    basket = ForeignKeyField(Basket)\n\n\nclass NodeTag(TestModel):\n    tag = TextField()\n\nclass Node(TestModel):\n    name = TextField()\n    tag = ForeignKeyField(NodeTag)\n    parent = ForeignKeyField('self', null=True, backref='children')\n\n\nclass TestModelToDict(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User, Tweet, Tag, TweetTag]\n\n    def setUp(self):\n        super(TestModelToDict, self).setUp()\n        self.user = User.create(username='peewee')\n\n    @requires_models(Node, NodeTag)\n    def test_self_referential(self):\n        a, b = [NodeTag.create(tag=tag) for tag in 'ab']\n        root = Node.create(name='root', tag=a)\n        n1 = Node.create(name='n1', parent=root, tag=a)\n        n2 = Node.create(name='n2', parent=root, tag=b)\n        Parent = Node.alias('parent')\n        ParentTag = NodeTag.alias('parent_tag')\n\n        def assertSerialization(n, expected):\n            obj = (Node\n                   .select(Node, NodeTag, Parent, ParentTag)\n                   .join_from(Node, NodeTag, JOIN.LEFT_OUTER)\n                   .join_from(Node, Parent, JOIN.LEFT_OUTER)\n                   .join_from(Parent, ParentTag, JOIN.LEFT_OUTER)\n                   .where(Node.name == n)\n                   .first())\n\n            self.assertEqual(model_to_dict(obj, recurse=True), expected)\n\n        assertSerialization('n1', {\n            'id': n1.id,\n            'name': 'n1',\n            'parent': {'id': root.id, 'name': 'root',\n                       'tag': {'id': a.id, 'tag': 'a'}},\n            'tag': {'id': a.id, 'tag': 'a'}})\n        assertSerialization('n2', {\n            'id': n2.id,\n            'name': 'n2',\n            'parent': {'id': root.id, 'name': 'root',\n                       'tag': {'id': a.id, 'tag': 'a'}},\n            'tag': {'id': b.id, 'tag': 'b'}})\n        assertSerialization('root', {\n            'id': root.id,\n            'name': 'root',\n            'parent': None,\n            'tag': {'id': a.id, 'tag': 'a'}})\n\n    def test_simple(self):\n        with self.assertQueryCount(0):\n            self.assertEqual(model_to_dict(self.user), {\n                'id': self.user.id,\n                'username': 'peewee'})\n\n    def test_simple_recurse(self):\n        tweet = Tweet.create(user=self.user, content='t1')\n        with self.assertQueryCount(0):\n            self.assertEqual(model_to_dict(tweet), {\n                'id': tweet.id,\n                'content': tweet.content,\n                'user': {\n                    'id': self.user.id,\n                    'username': 'peewee'}})\n\n        with self.assertQueryCount(0):\n            self.assertEqual(model_to_dict(tweet, recurse=False), {\n                'id': tweet.id,\n                'content': tweet.content,\n                'user': self.user.id})\n\n    def test_simple_backref(self):\n        with self.assertQueryCount(1):\n            self.assertEqual(model_to_dict(self.user, backrefs=True), {\n                'id': self.user.id,\n                'tweets': [],\n                'username': 'peewee'})\n\n        tweet = Tweet.create(user=self.user, content='t0')\n\n        # Two queries, one for tweets, one for tweet-tags.\n        with self.assertQueryCount(2):\n            self.assertEqual(model_to_dict(self.user, backrefs=True), {\n                'id': self.user.id,\n                'username': 'peewee',\n                'tweets': [{'id': tweet.id, 'content': 't0',\n                            'tweettag_set': []}]})\n\n    def test_recurse_and_backrefs(self):\n        tweet = Tweet.create(user=self.user, content='t0')\n        with self.assertQueryCount(1):\n            self.assertEqual(model_to_dict(tweet, backrefs=True), {\n                'id': tweet.id,\n                'content': 't0',\n                'tweettag_set': [],\n                'user': {'id': self.user.id, 'username': 'peewee'}})\n\n    @requires_models(Category)\n    def test_recursive_fk(self):\n        root = Category.create(name='root')\n        child = Category.create(name='child', parent=root)\n        grandchild = Category.create(name='grandchild', parent=child)\n\n        with self.assertQueryCount(0):\n            for recurse in (True, False):\n                self.assertEqual(model_to_dict(root, recurse=recurse), {\n                    'name': 'root',\n                    'parent': None})\n\n        with self.assertQueryCount(1):\n            self.assertEqual(model_to_dict(root, backrefs=True), {\n                'name': 'root',\n                'parent': None,\n                'children': [{'name': 'child'}]})\n\n        with self.assertQueryCount(1):\n            self.assertEqual(model_to_dict(root, backrefs=True), {\n                'name': 'root',\n                'parent': None,\n                'children': [{'name': 'child'}]})\n\n        with self.assertQueryCount(1):\n            self.assertEqual(model_to_dict(child, backrefs=True), {\n                'name': 'child',\n                'parent': {'name': 'root'},\n                'children': [{'name': 'grandchild'}]})\n\n        with self.assertQueryCount(0):\n            self.assertEqual(model_to_dict(child, backrefs=False), {\n                'name': 'child',\n                'parent': {'name': 'root'}})\n\n    def test_manytomany(self):\n        tweet = Tweet.create(user=self.user, content='t0')\n        tag1 = Tag.create(tag='t1')\n        tag2 = Tag.create(tag='t2')\n        Tag.create(tag='tx')\n        TweetTag.create(tweet=tweet, tag=tag1)\n        TweetTag.create(tweet=tweet, tag=tag2)\n\n        with self.assertQueryCount(4):\n            self.assertEqual(model_to_dict(self.user, backrefs=True), {\n                'id': self.user.id,\n                'username': 'peewee',\n                'tweets': [{\n                    'id': tweet.id,\n                    'content': 't0',\n                    'tweettag_set': [\n                        {'tag': {'id': tag1.id, 'tag': 't1'}},\n                        {'tag': {'id': tag2.id, 'tag': 't2'}}]}]})\n\n    @requires_models(Label, Gallery, GalleryLabel, Owner)\n    def test_manytomany_field(self):\n        data = (\n            ('charlie', 'family', ('nuggie', 'bearbe')),\n            ('charlie', 'pets', ('huey', 'zaizee', 'beanie')),\n            ('peewee', 'misc', ('nuggie', 'huey')))\n        for owner_name, gallery, labels in data:\n            owner, _ = Owner.get_or_create(name=owner_name)\n            gallery = Gallery.create(name=gallery, owner=owner)\n            label_objects = [Label.get_or_create(label=l)[0] for l in labels]\n            gallery.labels.add(label_objects)\n\n        query = (Gallery\n                 .select(Gallery, Owner)\n                 .join(Owner)\n                 .switch(Gallery)\n                 .join(GalleryLabel)\n                 .join(Label)\n                 .where(Label.label == 'nuggie')\n                 .order_by(Gallery.id))\n        rows = [model_to_dict(gallery, backrefs=True, manytomany=True)\n                for gallery in query]\n        self.assertEqual(rows, [\n            {\n                'id': 1,\n                'name': 'family',\n                'owner': {'id': 1, 'name': 'charlie'},\n                'labels': [{'id': 1, 'label': 'nuggie'},\n                           {'id': 2, 'label': 'bearbe'}],\n            },\n            {\n                'id': 3,\n                'name': 'misc',\n                'owner': {'id': 2, 'name': 'peewee'},\n                'labels': [{'id': 1, 'label': 'nuggie'},\n                           {'id': 3, 'label': 'huey'}],\n            }])\n\n    @requires_models(Student, Course, StudentCourse)\n    def test_manytomany_deferred(self):\n        data = (\n            ('s1', ('ca', 'cb', 'cc')),\n            ('s2', ('cb', 'cd')),\n            ('s3', ()))\n        c = {}\n        for student, courses in data:\n            s = Student.create(name=student)\n            for course in courses:\n                if course not in c:\n                    c[course] = Course.create(name=course)\n                StudentCourse.create(student=s, course=c[course])\n\n        query = Student.select().order_by(Student.name)\n        data = []\n        for user in query:\n            user_dict = model_to_dict(user, manytomany=True)\n            user_dict['courses'].sort(key=operator.itemgetter('id'))\n            data.append(user_dict)\n\n        self.assertEqual(data, [\n            {'id': 1, 'name': 's1', 'courses': [\n                {'id': 1, 'name': 'ca'},\n                {'id': 2, 'name': 'cb'},\n                {'id': 3, 'name': 'cc'}]},\n            {'id': 2, 'name': 's2', 'courses': [\n                {'id': 2, 'name': 'cb'},\n                {'id': 4, 'name': 'cd'}]},\n            {'id': 3, 'name': 's3', 'courses': []}])\n\n        query = Course.select().order_by(Course.name)\n        data = []\n        for course in query:\n            course_dict = model_to_dict(course, manytomany=True)\n            course_dict['students'].sort(key=operator.itemgetter('id'))\n            data.append(course_dict)\n\n        self.assertEqual(data, [\n            {'id': 1, 'name': 'ca', 'students': [\n                {'id': 1, 'name': 's1'}]},\n            {'id': 2, 'name': 'cb', 'students': [\n                {'id': 1, 'name': 's1'},\n                {'id': 2, 'name': 's2'}]},\n            {'id': 3, 'name': 'cc', 'students': [\n                {'id': 1, 'name': 's1'}]},\n            {'id': 4, 'name': 'cd', 'students': [\n                {'id': 2, 'name': 's2'}]}])\n\n    def test_recurse_max_depth(self):\n        t0, t1, t2 = [Tweet.create(user=self.user, content='t%s' % i)\n                      for i in range(3)]\n        tag0, tag1 = [Tag.create(tag=t) for t in ['tag0', 'tag1']]\n        TweetTag.create(tweet=t0, tag=tag0)\n        TweetTag.create(tweet=t0, tag=tag1)\n        TweetTag.create(tweet=t1, tag=tag1)\n\n        data = model_to_dict(self.user, recurse=True, backrefs=True)\n        self.assertEqual(data, {\n            'id': self.user.id,\n            'username': 'peewee',\n            'tweets': [\n                {'id': t0.id, 'content': 't0', 'tweettag_set': [\n                    {'tag': {'tag': 'tag0', 'id': tag0.id}},\n                    {'tag': {'tag': 'tag1', 'id': tag1.id}},\n                ]},\n                {'id': t1.id, 'content': 't1', 'tweettag_set': [\n                    {'tag': {'tag': 'tag1', 'id': tag1.id}},\n                ]},\n                {'id': t2.id, 'content': 't2', 'tweettag_set': []},\n            ]})\n\n        data = model_to_dict(self.user, recurse=True, backrefs=True,\n                             max_depth=2)\n        self.assertEqual(data, {\n            'id': self.user.id,\n            'username': 'peewee',\n            'tweets': [\n                {'id': t0.id, 'content': 't0', 'tweettag_set': [\n                    {'tag': tag0.id}, {'tag': tag1.id},\n                ]},\n                {'id': t1.id, 'content': 't1', 'tweettag_set': [\n                    {'tag': tag1.id},\n                ]},\n                {'id': t2.id, 'content': 't2', 'tweettag_set': []},\n            ]})\n\n        data = model_to_dict(self.user, recurse=True, backrefs=True,\n                             max_depth=1)\n        self.assertEqual(data, {\n            'id': self.user.id,\n            'username': 'peewee',\n            'tweets': [\n                {'id': t0.id, 'content': 't0'},\n                {'id': t1.id, 'content': 't1'},\n                {'id': t2.id, 'content': 't2'}]})\n\n        self.assertEqual(model_to_dict(self.user, recurse=True, backrefs=True,\n                                       max_depth=0),\n                         {'id': self.user.id, 'username': 'peewee'})\n\n    def test_only(self):\n        username_dict = {'username': 'peewee'}\n        self.assertEqual(model_to_dict(self.user, only=[User.username]),\n                         username_dict)\n\n        self.assertEqual(\n            model_to_dict(self.user, backrefs=True, only=[User.username]),\n            username_dict)\n\n        tweet = Tweet.create(user=self.user, content='t0')\n        tweet_dict = {'content': 't0', 'user': {'username': 'peewee'}}\n        field_list = [Tweet.content, Tweet.user, User.username]\n        self.assertEqual(model_to_dict(tweet, only=field_list),\n                         tweet_dict)\n        self.assertEqual(model_to_dict(tweet, backrefs=True, only=field_list),\n                         tweet_dict)\n\n        tweet_dict['user'] = self.user.id\n        self.assertEqual(model_to_dict(tweet, backrefs=True, recurse=False,\n                                       only=field_list),\n                         tweet_dict)\n\n    def test_exclude(self):\n        self.assertEqual(model_to_dict(self.user, exclude=[User.id]),\n                         {'username': 'peewee'})\n\n        # Exclude the foreign key using FK field and backref.\n        self.assertEqual(model_to_dict(self.user, backrefs=True,\n                                       exclude=[User.id, Tweet.user]),\n                         {'username': 'peewee'})\n        self.assertEqual(model_to_dict(self.user, backrefs=True,\n                                       exclude=[User.id, User.tweets]),\n                         {'username': 'peewee'})\n\n        tweet = Tweet.create(user=self.user, content='t0')\n        fields = [Tweet.tweettag_set, Tweet.id, Tweet.user]\n        self.assertEqual(model_to_dict(tweet, backrefs=True, exclude=fields),\n                         {'content': 't0'})\n        fields[-1] = User.id\n        self.assertEqual(model_to_dict(tweet, backrefs=True, exclude=fields),\n                         {'content': 't0', 'user': {'username': 'peewee'}})\n\n    def test_extra_attrs(self):\n        with self.assertQueryCount(0):\n            extra = ['name_hash']\n            self.assertEqual(model_to_dict(self.user, extra_attrs=extra), {\n                'id': self.user.id,\n                'username': 'peewee',\n                'name_hash': 5})\n\n        with self.assertQueryCount(0):\n            self.assertRaises(AttributeError, model_to_dict, self.user,\n                              extra_attrs=['xx'])\n\n    def test_fields_from_query(self):\n        User.delete().execute()\n        for i in range(3):\n            user = User.create(username='u%d' % i)\n            for x in range(i + 1):\n                Tweet.create(user=user, content='%s-%s' % (user.username, x))\n\n        query = (User\n                 .select(User.username, fn.COUNT(Tweet.id).alias('ct'))\n                 .join(Tweet, JOIN.LEFT_OUTER)\n                 .group_by(User.username)\n                 .order_by(User.id))\n        with self.assertQueryCount(1):\n            u0, u1, u2 = list(query)\n            self.assertEqual(model_to_dict(u0, fields_from_query=query), {\n                'username': 'u0',\n                'ct': 1})\n            self.assertEqual(model_to_dict(u2, fields_from_query=query), {\n                'username': 'u2',\n                'ct': 3})\n\n        query = (Tweet\n                 .select(Tweet, User, SQL('1337').alias('magic'))\n                 .join(User)\n                 .order_by(Tweet.id)\n                 .limit(1))\n        with self.assertQueryCount(1):\n            tweet, = query\n            self.assertEqual(model_to_dict(tweet, fields_from_query=query), {\n                'id': tweet.id,\n                'content': 'u0-0',\n                'magic': 1337,\n                'user': {'id': tweet.user_id, 'username': 'u0'}})\n\n            self.assertEqual(model_to_dict(tweet, fields_from_query=query,\n                                           exclude=[User.id, Tweet.id]),\n                             {'magic': 1337, 'content': 'u0-0',\n                              'user': {'username': 'u0'}})\n\n    def test_fields_from_query_alias(self):\n        q = User.select(User.username.alias('name'))\n        res = q[0]\n        self.assertEqual(model_to_dict(res, fields_from_query=q),\n                         {'name': 'peewee'})\n\n        UA = User.alias()\n        q = UA.select(UA.username.alias('name'))\n        res = q[0]\n        self.assertEqual(model_to_dict(res, fields_from_query=q),\n                         {'name': 'peewee'})\n\n    def test_only_backref(self):\n        for i in range(3):\n            Tweet.create(user=self.user, content=str(i))\n\n        data = model_to_dict(self.user, backrefs=True, only=[\n            User.username,\n            User.tweets,\n            Tweet.content])\n        if 'tweets' in data:\n            data['tweets'].sort(key=lambda t: t['content'])\n        self.assertEqual(data, {\n            'username': 'peewee',\n            'tweets': [\n                {'content': '0'},\n                {'content': '1'},\n                {'content': '2'}]})\n\n    @requires_models(Host, Service, Device)\n    def test_model_to_dict_disabled_backref(self):\n        host = Host.create(name='pi')\n        Device.create(host=host, name='raspberry pi')\n        Service.create(host=host, name='ssh')\n        Service.create(host=host, name='vpn')\n\n        data = model_to_dict(host, recurse=True, backrefs=True)\n        services = sorted(data.pop('services'), key=operator.itemgetter('id'))\n        self.assertEqual(data, {'id': 1, 'name': 'pi'})\n        self.assertEqual(services, [\n            {'id': 1, 'name': 'ssh'},\n            {'id': 2, 'name': 'vpn'}])\n\n    @requires_models(Basket, Item)\n    def test_empty_vs_null_fk(self):\n        b = Basket.create(id=0)\n        i = Item.create(id=0, basket=b)\n\n        data = model_to_dict(i)\n        self.assertEqual(data, {'id': 0, 'basket': {'id': 0}})\n\n        data = model_to_dict(i, recurse=False)\n        self.assertEqual(data, {'id': 0, 'basket': 0})\n\n\nclass TestDictToModel(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [User, Tweet, Tag, TweetTag]\n\n    def setUp(self):\n        super(TestDictToModel, self).setUp()\n        self.user = User.create(username='peewee')\n\n    def test_simple(self):\n        data = {'username': 'peewee', 'id': self.user.id}\n        inst = dict_to_model(User, data)\n        self.assertTrue(isinstance(inst, User))\n        self.assertEqual(inst.username, 'peewee')\n        self.assertEqual(inst.id, self.user.id)\n\n    def test_update_model_from_dict(self):\n        data = {'content': 'tweet', 'user': {'username': 'zaizee'}}\n        with self.assertQueryCount(0):\n            user = User(id=3, username='orig')\n            tweet = Tweet(id=4, content='orig', user=user)\n\n            obj = update_model_from_dict(tweet, data)\n\n        self.assertEqual(obj.id, 4)\n        self.assertEqual(obj.content, 'tweet')\n        self.assertEqual(obj.user.id, 3)\n        self.assertEqual(obj.user.username, 'zaizee')\n\n    def test_related(self):\n        data = {\n            'id': 2,\n            'content': 'tweet-1',\n            'user': {'id': self.user.id, 'username': 'peewee'}}\n\n        with self.assertQueryCount(0):\n            inst = dict_to_model(Tweet, data)\n            self.assertTrue(isinstance(inst, Tweet))\n            self.assertEqual(inst.id, 2)\n            self.assertEqual(inst.content, 'tweet-1')\n            self.assertTrue(isinstance(inst.user, User))\n            self.assertEqual(inst.user.id, self.user.id)\n            self.assertEqual(inst.user.username, 'peewee')\n\n        data['user'] = self.user.id\n\n        with self.assertQueryCount(0):\n            inst = dict_to_model(Tweet, data)\n\n        with self.assertQueryCount(1):\n            self.assertEqual(inst.user, self.user)\n\n    def test_backrefs(self):\n        data = {\n            'id': self.user.id,\n            'username': 'peewee',\n            'tweets': [\n                {'id': 1, 'content': 't1'},\n                {'id': 2, 'content': 't2'},\n            ]}\n\n        with self.assertQueryCount(0):\n            inst = dict_to_model(User, data)\n            self.assertEqual(inst.id, self.user.id)\n            self.assertEqual(inst.username, 'peewee')\n            self.assertTrue(isinstance(inst.tweets, list))\n\n            t1, t2 = inst.tweets\n            self.assertEqual(t1.id, 1)\n            self.assertEqual(t1.content, 't1')\n            self.assertEqual(t1.user, self.user)\n\n            self.assertEqual(t2.id, 2)\n            self.assertEqual(t2.content, 't2')\n            self.assertEqual(t2.user, self.user)\n\n    def test_unknown_attributes(self):\n        data = {\n            'id': self.user.id,\n            'username': 'peewee',\n            'xx': 'does not exist'}\n        self.assertRaises(AttributeError, dict_to_model, User, data)\n\n        inst = dict_to_model(User, data, ignore_unknown=True)\n        self.assertEqual(inst.xx, 'does not exist')\n\n    def test_ignore_id_attribute(self):\n        class Register(Model):\n            key = CharField(primary_key=True)\n\n        data = {'id': 100, 'key': 'k1'}\n        self.assertRaises(AttributeError, dict_to_model, Register, data)\n\n        inst = dict_to_model(Register, data, ignore_unknown=True)\n        self.assertEqual(inst.__data__, {'key': 'k1'})\n\n        class Base(Model):\n            class Meta:\n                primary_key = False\n\n        class Register2(Model):\n            key = CharField(primary_key=True)\n\n        self.assertRaises(AttributeError, dict_to_model, Register2, data)\n\n        inst = dict_to_model(Register2, data, ignore_unknown=True)\n        self.assertEqual(inst.__data__, {'key': 'k1'})\n\n\nclass ReconnectMySQLDatabase(ReconnectMixin, MySQLDatabase):\n    def cursor(self, named_cursor=None):\n        cursor = super(ReconnectMySQLDatabase, self).cursor(named_cursor)\n\n        # The first (0th) query fails, as do all queries after the 2nd (1st).\n        if self._query_counter != 1:\n            def _fake_execute(self, *args):\n                raise OperationalError('2006')\n            cursor.execute = _fake_execute\n        self._query_counter += 1\n        return cursor\n\n    def close(self):\n        self._close_counter += 1\n        return super(ReconnectMySQLDatabase, self).close()\n\n    def _reset_mock(self):\n        self._close_counter = 0\n        self._query_counter = 0\n\n\n@requires_mysql\nclass TestReconnectMixin(DatabaseTestCase):\n    database = db_loader('mysql', db_class=ReconnectMySQLDatabase)\n\n    def test_reconnect_mixin_execute_sql(self):\n        # Verify initial state.\n        self.database._reset_mock()\n        self.assertEqual(self.database._close_counter, 0)\n\n        sql = 'select 1 + 1'\n        curs = self.database.execute_sql(sql)\n        self.assertEqual(curs.fetchone(), (2,))\n        self.assertEqual(self.database._close_counter, 1)\n\n        # Due to how we configured our mock, our queries are now failing and we\n        # can verify a reconnect is occuring *AND* the exception is propagated.\n        self.assertRaises(OperationalError, self.database.execute_sql, sql)\n        self.assertEqual(self.database._close_counter, 2)\n\n        # We reset the mock counters. The first query we execute will fail. The\n        # second query will succeed (which happens automatically, thanks to the\n        # retry logic).\n        self.database._reset_mock()\n        curs = self.database.execute_sql(sql)\n        self.assertEqual(curs.fetchone(), (2,))\n        self.assertEqual(self.database._close_counter, 1)\n\n\n    def test_reconnect_mixin_begin(self):\n        # Verify initial state.\n        self.database._reset_mock()\n        self.assertEqual(self.database._close_counter, 0)\n\n        with self.database.atomic():\n            self.assertTrue(self.database.in_transaction())\n            self.assertEqual(self.database._close_counter, 1)\n            # Prepare mock for commit call\n            self.database._query_counter = 1\n\n        # Due to how we configured our mock, our queries are now failing and we\n        # can verify a reconnect is occuring *AND* the exception is propagated.\n        self.assertRaises(OperationalError, self.database.atomic().__enter__)\n        self.assertEqual(self.database._close_counter, 2)\n        self.assertFalse(self.database.in_transaction())\n\n        # We reset the mock counters. The first query we execute will fail. The\n        # second query will succeed (which happens automatically, thanks to the\n        # retry logic).\n        self.database._reset_mock()\n        with self.database.atomic():\n            self.assertTrue(self.database.in_transaction())\n            self.assertEqual(self.database._close_counter, 1)\n\n            # Do not reconnect when nesting transactions\n            self.assertRaises(OperationalError, self.database.atomic().__enter__)\n            self.assertEqual(self.database._close_counter, 1)\n\n            # Prepare mock for commit call\n            self.database._query_counter = 1\n        self.assertFalse(self.database.in_transaction())\n\n\nclass MMA(TestModel):\n    key = TextField()\n    value = IntegerField()\n\nclass MMB(TestModel):\n    key = TextField()\n\nclass MMC(TestModel):\n    key = TextField()\n    value = IntegerField()\n    misc = TextField(null=True)\n\n\nclass TestResolveMultiModelQuery(ModelTestCase):\n    requires = [MMA, MMB, MMC]\n\n    def test_resolve_multimodel_query(self):\n        MMA.insert_many([('k0', 0), ('k1', 1)]).execute()\n        MMB.insert_many([('k10',), ('k11',)]).execute()\n        MMC.insert_many([('k20', 20, 'a'), ('k21', 21, 'b')]).execute()\n\n        mma = MMA.select(MMA.key, MMA.value)\n        mmb = MMB.select(MMB.key, Value(99).alias('value'))\n        mmc = MMC.select(MMC.key, MMC.value)\n        query = (mma | mmb | mmc).order_by(SQL('1'))\n        data = [obj for obj in resolve_multimodel_query(query)]\n\n        expected = [\n            MMA(key='k0', value=0), MMA(key='k1', value=1),\n            MMB(key='k10', value=99), MMB(key='k11', value=99),\n            MMC(key='k20', value=20), MMC(key='k21', value=21)]\n        self.assertEqual(len(data), len(expected))\n\n        for row, exp_row in zip(data, expected):\n            self.assertEqual(row.__class__, exp_row.__class__)\n            self.assertEqual(row.key, exp_row.key)\n            self.assertEqual(row.value, exp_row.value)\n\n\nts_database = get_in_memory_db()\n\nclass TSBase(Model):\n    class Meta:\n        database = ts_database\n        model_metadata_class = ThreadSafeDatabaseMetadata\n\nclass TSReg(TSBase):\n    key = TextField()\n\nclass TestThreadSafeDatabaseMetadata(BaseTestCase):\n    def setUp(self):\n        super(TestThreadSafeDatabaseMetadata, self).setUp()\n        ts_database.create_tables([TSReg])\n\n    def test_threadsafe_database_metadata(self):\n        self.assertTrue(isinstance(TSReg._meta, ThreadSafeDatabaseMetadata))\n        self.assertEqual(TSReg._meta.database, ts_database)\n\n        t1 = TSReg.create(key='k1')\n        t1_db = TSReg.get(TSReg.key == 'k1')\n        self.assertEqual(t1.id, t1_db.id)\n\n    def test_swap_database(self):\n        d1 = get_in_memory_db()\n        d2 = get_in_memory_db()\n\n        class M(TSBase):\n            pass\n\n        def swap_db():\n            self.assertEqual(M._meta.database, ts_database)\n            d1.bind([M])\n            self.assertEqual(M._meta.database, d1)\n            with d2.bind_ctx([M]):\n                self.assertEqual(M._meta.database, d2)\n            self.assertEqual(M._meta.database, d1)\n\n        self.assertEqual(M._meta.database, ts_database)\n\n        # From a separate thread, swap the database and verify it works\n        # correctly.\n        t = threading.Thread(target=swap_db)\n        t.start() ; t.join()\n\n        # In the main thread the original database has not been altered.\n        self.assertEqual(M._meta.database, ts_database)\n\n    def test_preserve_original_db(self):\n        outputs = []\n        d1 = get_in_memory_db()\n        d2 = get_in_memory_db()\n\n        class M(TSBase):\n            class Meta:\n                database = d1\n\n        def swap_db():\n            self.assertTrue(M._meta.database is d1)\n            with d2.bind_ctx([M]):\n                self.assertTrue(M._meta.database is d2)\n            self.assertTrue(M._meta.database is d1)\n            d2.bind([M])  # Now bind to d2 and leave it bound.\n            self.assertTrue(M._meta.database is d2)\n\n        # From a separate thread, swap the database and verify it works\n        # correctly.\n        threads = [threading.Thread(target=swap_db) for _ in range(20)]\n        for t in threads: t.start()\n        for t in threads: t.join()\n\n        # In the main thread the original database has not been altered.\n        self.assertTrue(M._meta.database is d1)\n\n\nclass TIW(TestModel):\n    key = CharField()\n    value = IntegerField(default=0)\n    extra = IntegerField(default=lambda: 1)\n\n\nclass TestInsertWhere(ModelTestCase):\n    requires = [User, Tweet, TIW]\n\n    def test_insert_where(self):\n        ua, ub = [User.create(username=n) for n in 'ab']\n\n        def _insert_where(user, content):\n            cond = (Tweet.select()\n                    .where(Tweet.user == user, Tweet.content == content))\n            where = ~fn.EXISTS(cond)\n            iq = insert_where(Tweet, {\n                Tweet.user: user,\n                Tweet.content: content},\n                where=where)\n            return 1 if iq.execute() else 0\n\n        self.assertEqual(_insert_where(ua, 't1'), 1)\n        self.assertEqual(_insert_where(ua, 't2'), 1)\n        self.assertEqual(_insert_where(ua, 't1'), 0)\n        self.assertEqual(_insert_where(ua, 't2'), 0)\n        self.assertEqual(_insert_where(ub, 't1'), 1)\n        self.assertEqual(_insert_where(ub, 't2'), 1)\n\n    def test_insert_where_defaults(self):\n        TIW.create(key='k1', value=1, extra=2)\n        def _insert_where(key):\n            where = ~fn.EXISTS(TIW.select().where(TIW.key == key))\n            iq = insert_where(TIW, {TIW.key: key}, where)\n            return 1 if iq.execute() else 0\n\n        self.assertEqual(_insert_where('k2'), 1)\n        self.assertEqual(_insert_where('k1'), 0)\n        self.assertEqual(_insert_where('k2'), 0)\n        tiw = TIW.get(TIW.key == 'k2')\n        self.assertEqual(tiw.value, 0)\n        self.assertEqual(tiw.extra, 1)\n"
  },
  {
    "path": "tests/signals.py",
    "content": "from peewee import *\nfrom playhouse import signals\n\nfrom .base import get_in_memory_db\nfrom .base import ModelTestCase\n\n\nclass BaseSignalModel(signals.Model):\n    pass\n\nclass A(BaseSignalModel):\n    a = TextField(default='')\n\nclass B(BaseSignalModel):\n    b = TextField(default='')\n\nclass SubB(B): pass\n\n\nclass TestSignals(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [A, B, SubB]\n\n    def tearDown(self):\n        super(TestSignals, self).tearDown()\n        signals.pre_save._flush()\n        signals.post_save._flush()\n        signals.pre_delete._flush()\n        signals.post_delete._flush()\n        signals.pre_init._flush()\n\n    def test_pre_save(self):\n        state = []\n        @signals.pre_save()\n        def pre_save(sender, instance, created):\n            state.append((sender, instance, instance._pk, created))\n\n        a = A()\n        self.assertEqual(a.save(), 1)\n        self.assertEqual(state, [(A, a, None, True)])\n\n        self.assertEqual(a.save(), 1)\n        self.assertTrue(a.id is not None)\n        self.assertEqual(len(state), 2)\n        self.assertEqual(state[-1], (A, a, a.id, False))\n\n    def test_post_save(self):\n        state = []\n        @signals.post_save()\n        def post_save(sender, instance, created):\n            state.append((sender, instance, instance._pk, created))\n        a = A()\n        a.save()\n\n        self.assertTrue(a.id is not None)\n        self.assertEqual(state, [(A, a, a.id, True)])\n\n        a.save()\n        self.assertEqual(len(state), 2)\n        self.assertEqual(state[-1], (A, a, a.id, False))\n\n    def test_pre_delete(self):\n        state = []\n        @signals.pre_delete()\n        def pre_delete(sender, instance):\n            state.append((sender, instance, A.select().count()))\n\n        a = A.create()\n        self.assertEqual(a.delete_instance(), 1)\n        self.assertEqual(state, [(A, a, 1)])\n\n    def test_post_delete(self):\n        state = []\n        @signals.post_delete()\n        def post_delete(sender, instance):\n            state.append((sender, instance, A.select().count()))\n\n        a = A.create()\n        a.delete_instance()\n        self.assertEqual(state, [(A, a, 0)])\n\n    def test_pre_init(self):\n        state = []\n        A.create(a='a')\n\n        @signals.pre_init()\n        def pre_init(sender, instance):\n            state.append((sender, instance.a))\n\n        A.get()\n        self.assertEqual(state, [(A, 'a')])\n\n    def test_sender(self):\n        state = []\n\n        @signals.post_save(sender=A)\n        def post_save(sender, instance, created):\n            state.append(instance)\n\n        m = A.create()\n        self.assertEqual(state, [m])\n\n        m2 = B.create()\n        self.assertEqual(state, [m])\n\n    def test_connect_disconnect(self):\n        state = []\n\n        @signals.post_save(sender=A)\n        def post_save(sender, instance, created):\n            state.append(instance)\n\n        a = A.create()\n        self.assertEqual(state, [a])\n\n        # Signal was registered with a specific sender, so this fails.\n        self.assertRaises(ValueError, signals.post_save.disconnect, post_save)\n\n        # Disconnect signal, specifying sender.\n        signals.post_save.disconnect(post_save, sender=A)\n\n        # Signal handler has been unregistered.\n        a2 = A.create()\n        self.assertEqual(state, [a])\n\n        # Re-connect without specifying sender.\n        signals.post_save.connect(post_save)\n        a3 = A.create()\n        self.assertEqual(state, [a, a3])\n\n        # Signal was not registered with a sender, so this fails.\n        self.assertRaises(ValueError, signals.post_save.disconnect, post_save,\n                          sender=A)\n        signals.post_save.disconnect(post_save)\n\n    def test_function_reuse(self):\n        state = []\n\n        @signals.post_save(sender=A)\n        def post_save(sender, instance, created):\n            state.append(instance)\n\n        # Connect function for sender=B as well.\n        signals.post_save(sender=B)(post_save)\n\n        a = A.create()\n        b = B.create()\n        self.assertEqual(state, [a, b])\n\n    def test_subclass_instance_receive_signals(self):\n        state = []\n\n        @signals.post_save(sender=B)\n        def post_save(sender, instance, created):\n            state.append(instance)\n\n        b = SubB.create()\n        assert b in state\n\n    def test_disconnect_issue_2687(self):\n        state = []\n\n        # Same sender.\n        @signals.post_save(sender=A)\n        def sig1(sender, instance, created):\n            state.append((1, instance.a))\n\n        @signals.post_save(sender=A)\n        def sig2(sender, instance, created):\n            state.append((2, instance.a))\n\n        A.create(a='a1')\n        self.assertEqual(state, [(1, 'a1'), (2, 'a1')])\n        signals.post_save.disconnect(name='sig1', sender=A)\n\n        A.create(a='a2')\n        self.assertEqual(state, [(1, 'a1'), (2, 'a1'), (2, 'a2')])\n        signals.post_save.disconnect(name='sig2', sender=A)\n\n        A.create(a='a3')\n        self.assertEqual(state, [(1, 'a1'), (2, 'a1'), (2, 'a2')])\n\n        signals.post_save(name='s1')(sig1)\n        signals.post_save(name='s2', sender=A)(sig2)\n        state = state[:0]  # Clear state, 2.7 compat.\n\n        A.create(a='a4')\n        self.assertEqual(state, [(1, 'a4'), (2, 'a4')])\n        signals.post_save.disconnect(name='s1')\n\n        A.create(a='a5')\n        self.assertEqual(state, [(1, 'a4'), (2, 'a4'), (2, 'a5')])\n        signals.post_save.disconnect(name='s2', sender=A)\n\n        A.create(a='a6')\n        self.assertEqual(state, [(1, 'a4'), (2, 'a4'), (2, 'a5')])\n\n\nclass NoPK(BaseSignalModel):\n    val = IntegerField(index=True)\n    class Meta:\n        primary_key = False\n\n\nclass TestSaveNoPrimaryKey(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [NoPK]\n\n    def test_save_no_pk(self):\n        accum = [0]\n\n        @signals.pre_save(sender=NoPK)\n        @signals.post_save(sender=NoPK)\n        def save_hook(sender, instance, created):\n            accum[0] += 1\n\n        obj = NoPK.create(val=1)\n        self.assertEqual(obj.val, 1)\n\n        obj_db = NoPK.get(NoPK.val == 1)\n        self.assertEqual(obj_db.val, 1)\n        self.assertEqual(accum[0], 2)\n"
  },
  {
    "path": "tests/sql.py",
    "content": "import datetime\nimport re\n\nfrom peewee import *\nfrom peewee import Expression\nfrom peewee import Function\nfrom peewee import query_to_string\n\nfrom .base import BaseTestCase\nfrom .base import TestModel\nfrom .base import db\nfrom .base import requires_mysql\nfrom .base import requires_sqlite\nfrom .base import __sql__\n\n\nUser = Table('users')\nTweet = Table('tweets')\nPerson = Table('person', ['id', 'name', 'dob'], primary_key='id')\nNote = Table('note', ['id', 'person_id', 'content'])\n\n\nclass TestSelectQuery(BaseTestCase):\n    def test_select(self):\n        query = (User\n                 .select(User.c.id, User.c.username)\n                 .where(User.c.username == 'foo'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" '\n            'FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" = ?)'), ['foo'])\n\n        query = (User\n                 .select(User.c['id'], User.c['username'])\n                 .where(User.c['username'] == 'test'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" '\n            'FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" = ?)'), ['test'])\n\n    def test_select_extend(self):\n        query = User.select(User.c.id, User.c.username)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" FROM \"users\" AS \"t1\"'), [])\n\n        query = query.select(User.c.username, User.c.is_admin)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\", \"t1\".\"is_admin\" FROM \"users\" AS \"t1\"'),\n            [])\n\n        query = query.select_extend(User.c.is_active, User.c.id)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\", \"t1\".\"is_admin\", \"t1\".\"is_active\", '\n            '\"t1\".\"id\" FROM \"users\" AS \"t1\"'), [])\n\n    def test_selected_columns(self):\n        query = (User\n                 .select(User.c.id, User.c.username, fn.COUNT(Tweet.c.id))\n                 .join(Tweet, JOIN.LEFT_OUTER,\n                       on=(User.c.id == Tweet.c.user_id)))\n        # NOTE: because of operator overloads for equality we have to test by\n        # asserting the attributes of the selected cols.\n        c_id, c_username, c_ct = query.selected_columns\n        self.assertEqual(c_id.name, 'id')\n        self.assertTrue(c_id.source is User)\n        self.assertEqual(c_username.name, 'username')\n        self.assertTrue(c_username.source is User)\n        self.assertTrue(isinstance(c_ct, Function))\n        self.assertEqual(c_ct.name, 'COUNT')\n        c_tid, = c_ct.arguments\n        self.assertEqual(c_tid.name, 'id')\n        self.assertTrue(c_tid.source is Tweet)\n\n        query.selected_columns = (User.c.username,)\n        c_username, = query.selected_columns\n        self.assertEqual(c_username.name, 'username')\n        self.assertTrue(c_username.source is User)\n\n    def test_select_explicit_columns(self):\n        query = (Person\n                 .select()\n                 .where(Person.dob < datetime.date(1980, 1, 1)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\", \"t1\".\"dob\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"dob\" < ?)'), [datetime.date(1980, 1, 1)])\n\n    def test_select_in_list_of_values(self):\n        names_vals = [\n            ['charlie', 'huey'],\n            ('charlie', 'huey'),\n            set(('charlie', 'huey')),\n            frozenset(('charlie', 'huey')),\n            (x for x in ('charlie', 'huey'))]\n\n        for names in names_vals:\n            query = (Person\n                     .select()\n                     .where(Person.name.in_(names)))\n            sql, params = Context().sql(query).query()\n            self.assertEqual(sql, (\n                'SELECT \"t1\".\"id\", \"t1\".\"name\", \"t1\".\"dob\" '\n                'FROM \"person\" AS \"t1\" '\n                'WHERE (\"t1\".\"name\" IN (?, ?))'))\n            self.assertEqual(sorted(params), ['charlie', 'huey'])\n\n        query = (Person\n                 .select()\n                 .where(Person.id.in_(range(1, 10, 2))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\", \"t1\".\"dob\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" IN (?, ?, ?, ?, ?))'), [1, 3, 5, 7, 9])\n\n    def test_select_subselect_function(self):\n        # For functions whose only argument is a subquery, we do not need to\n        # include additional parentheses -- in fact, some databases will report\n        # a syntax error if we do.\n        exists = fn.EXISTS(Tweet\n                           .select(Tweet.c.id)\n                           .where(Tweet.c.user_id == User.c.id))\n        query = User.select(User.c.username, exists.alias('has_tweet'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\", EXISTS('\n            'SELECT \"t2\".\"id\" FROM \"tweets\" AS \"t2\" '\n            'WHERE (\"t2\".\"user_id\" = \"t1\".\"id\")) AS \"has_tweet\" '\n            'FROM \"users\" AS \"t1\"'), [])\n\n        # If the function has more than one argument, we need to wrap the\n        # subquery in parentheses.\n        Stat = Table('stat', ['id', 'val'])\n        SA = Stat.alias('sa')\n        subq = SA.select(fn.SUM(SA.val).alias('val_sum'))\n        query = Stat.select(fn.COALESCE(subq, 0))\n        self.assertSQL(query, (\n            'SELECT COALESCE(('\n            'SELECT SUM(\"sa\".\"val\") AS \"val_sum\" FROM \"stat\" AS \"sa\"'\n            '), ?) FROM \"stat\" AS \"t1\"'), [0])\n\n    def test_subquery_in_select_sql(self):\n        subq = User.select(User.c.id).where(User.c.username == 'huey')\n        query = Tweet.select(Tweet.c.content,\n                             Tweet.c.user_id.in_(subq).alias('is_huey'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\", (\"t1\".\"user_id\" IN ('\n            'SELECT \"t2\".\"id\" FROM \"users\" AS \"t2\" WHERE (\"t2\".\"username\" = ?)'\n            ')) AS \"is_huey\" FROM \"tweets\" AS \"t1\"'), ['huey'])\n\n        # If we explicitly specify an alias, it will be included.\n        subq = subq.alias('sq')\n        query = Tweet.select(Tweet.c.content,\n                             Tweet.c.user_id.in_(subq).alias('is_huey'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\", (\"t1\".\"user_id\" IN ('\n            'SELECT \"t2\".\"id\" FROM \"users\" AS \"t2\" WHERE (\"t2\".\"username\" = ?)'\n            ') AS \"sq\") AS \"is_huey\" FROM \"tweets\" AS \"t1\"'), ['huey'])\n\n    def test_subquery_in_select_expression_sql(self):\n        Point = Table('point', ('x', 'y'))\n        PA = Point.alias('pa')\n\n        subq = PA.select(fn.SUM(PA.y).alias('sa')).where(PA.x == Point.x)\n        query = (Point\n                 .select(Point.x, Point.y, subq.alias('sy'))\n                 .order_by(Point.x, Point.y))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"x\", \"t1\".\"y\", ('\n            'SELECT SUM(\"pa\".\"y\") AS \"sa\" FROM \"point\" AS \"pa\" '\n            'WHERE (\"pa\".\"x\" = \"t1\".\"x\")) AS \"sy\" '\n            'FROM \"point\" AS \"t1\" '\n            'ORDER BY \"t1\".\"x\", \"t1\".\"y\"'), [])\n\n    def test_star(self):\n        query = User.select(User.__star__)\n        self.assertSQL(query, ('SELECT \"t1\".* FROM \"users\" AS \"t1\"'), [])\n\n        query = (Tweet\n                 .select(Tweet.__star__, User.__star__)\n                 .join(User, on=(Tweet.c.user_id == User.c.id)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".*, \"t2\".* '\n            'FROM \"tweets\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\")'), [])\n\n        query = (Tweet\n                 .select(Tweet.__star__, User.c.id)\n                 .join(User, on=(Tweet.c.user_id == User.c.id)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".*, \"t2\".\"id\" '\n            'FROM \"tweets\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\")'), [])\n\n    def test_from_clause(self):\n        query = (Note\n                 .select(Note.content, Person.name)\n                 .from_(Note, Person)\n                 .where(Note.person_id == Person.id)\n                 .order_by(Note.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\", \"t2\".\"name\" '\n            'FROM \"note\" AS \"t1\", \"person\" AS \"t2\" '\n            'WHERE (\"t1\".\"person_id\" = \"t2\".\"id\") '\n            'ORDER BY \"t1\".\"id\"'), [])\n\n    def test_from_query(self):\n        inner = Person.select(Person.name)\n        query = (Person\n                 .select(Person.name)\n                 .from_(inner.alias('i1')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\" '\n            'FROM (SELECT \"t1\".\"name\" FROM \"person\" AS \"t1\") AS \"i1\"'), [])\n\n        PA = Person.alias('pa')\n        inner = PA.select(PA.name).alias('i1')\n        query = (Person\n                 .select(inner.c.name)\n                 .from_(inner)\n                 .order_by(inner.c.name))\n        self.assertSQL(query, (\n            'SELECT \"i1\".\"name\" '\n            'FROM (SELECT \"pa\".\"name\" FROM \"person\" AS \"pa\") AS \"i1\" '\n            'ORDER BY \"i1\".\"name\"'), [])\n\n    def test_join_explicit_columns(self):\n        query = (Note\n                 .select(Note.content)\n                 .join(Person, on=(Note.person_id == Person.id))\n                 .where(Person.name == 'charlie')\n                 .order_by(Note.id.desc()))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\" '\n            'FROM \"note\" AS \"t1\" '\n            'INNER JOIN \"person\" AS \"t2\" ON (\"t1\".\"person_id\" = \"t2\".\"id\") '\n            'WHERE (\"t2\".\"name\" = ?) '\n            'ORDER BY \"t1\".\"id\" DESC'), ['charlie'])\n\n    def test_multi_join(self):\n        Like = Table('likes')\n        LikeUser = User.alias('lu')\n        query = (Like\n                 .select(Tweet.c.content, User.c.username, LikeUser.c.username)\n                 .join(Tweet, on=(Like.c.tweet_id == Tweet.c.id))\n                 .join(User, on=(Tweet.c.user_id == User.c.id))\n                 .join(LikeUser, on=(Like.c.user_id == LikeUser.c.id))\n                 .where(LikeUser.c.username == 'charlie')\n                 .order_by(Tweet.c.timestamp))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\", \"t2\".\"username\", \"lu\".\"username\" '\n            'FROM \"likes\" AS \"t3\" '\n            'INNER JOIN \"tweets\" AS \"t1\" ON (\"t3\".\"tweet_id\" = \"t1\".\"id\") '\n            'INNER JOIN \"users\" AS \"t2\" ON (\"t1\".\"user_id\" = \"t2\".\"id\") '\n            'INNER JOIN \"users\" AS \"lu\" ON (\"t3\".\"user_id\" = \"lu\".\"id\") '\n            'WHERE (\"lu\".\"username\" = ?) '\n            'ORDER BY \"t1\".\"timestamp\"'), ['charlie'])\n\n    def test_correlated_subquery(self):\n        Employee = Table('employee', ['id', 'name', 'salary', 'dept'])\n        EA = Employee.alias('e2')\n        query = (Employee\n                 .select(Employee.id, Employee.name)\n                 .where(Employee.salary > (EA\n                                           .select(fn.AVG(EA.salary))\n                                           .where(EA.dept == Employee.dept))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\" '\n            'FROM \"employee\" AS \"t1\" '\n            'WHERE (\"t1\".\"salary\" > ('\n            'SELECT AVG(\"e2\".\"salary\") '\n            'FROM \"employee\" AS \"e2\" '\n            'WHERE (\"e2\".\"dept\" = \"t1\".\"dept\")))'), [])\n\n    def test_multiple_where(self):\n        \"\"\"Ensure multiple calls to WHERE are AND-ed together.\"\"\"\n        query = (Person\n                 .select(Person.name)\n                 .where(Person.dob < datetime.date(1980, 1, 1))\n                 .where(Person.dob > datetime.date(1950, 1, 1)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE ((\"t1\".\"dob\" < ?) AND (\"t1\".\"dob\" > ?))'),\n            [datetime.date(1980, 1, 1), datetime.date(1950, 1, 1)])\n\n    def test_orwhere(self):\n        query = (Person\n                 .select(Person.name)\n                 .orwhere(Person.dob > datetime.date(1980, 1, 1))\n                 .orwhere(Person.dob < datetime.date(1950, 1, 1)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE ((\"t1\".\"dob\" > ?) OR (\"t1\".\"dob\" < ?))'),\n            [datetime.date(1980, 1, 1), datetime.date(1950, 1, 1)])\n\n    def test_limit(self):\n        base = User.select(User.c.id)\n        self.assertSQL(base.limit(None), (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\"'), [])\n        self.assertSQL(base.limit(10), (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" LIMIT ?'), [10])\n        self.assertSQL(base.limit(10).offset(3), (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'LIMIT ? OFFSET ?'), [10, 3])\n        self.assertSQL(base.limit(0), (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" LIMIT ?'), [0])\n\n        self.assertSQL(base.offset(3), (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" OFFSET ?'), [3],\n            limit_max=None)\n        # Some databases do not support offset without corresponding LIMIT:\n        self.assertSQL(base.offset(3), (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" LIMIT ? OFFSET ?'), [-1, 3],\n            limit_max=-1)\n        self.assertSQL(base.limit(0).offset(3), (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" LIMIT ? OFFSET ?'), [0, 3],\n            limit_max=-1)\n\n    def test_simple_join(self):\n        query = (User\n                 .select(\n                     User.c.id,\n                     User.c.username,\n                     fn.COUNT(Tweet.c.id).alias('ct'))\n                 .join(Tweet, on=(Tweet.c.user_id == User.c.id))\n                 .group_by(User.c.id, User.c.username))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\", COUNT(\"t2\".\"id\") AS \"ct\" '\n            'FROM \"users\" AS \"t1\" '\n            'INNER JOIN \"tweets\" AS \"t2\" ON (\"t2\".\"user_id\" = \"t1\".\"id\") '\n            'GROUP BY \"t1\".\"id\", \"t1\".\"username\"'), [])\n\n    def test_subquery(self):\n        inner = (Tweet\n                 .select(fn.COUNT(Tweet.c.id).alias('ct'))\n                 .where(Tweet.c.user == User.c.id))\n        query = (User\n                 .select(User.c.username, inner.alias('iq'))\n                 .order_by(User.c.username))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\", '\n            '(SELECT COUNT(\"t2\".\"id\") AS \"ct\" '\n            'FROM \"tweets\" AS \"t2\" '\n            'WHERE (\"t2\".\"user\" = \"t1\".\"id\")) AS \"iq\" '\n            'FROM \"users\" AS \"t1\" ORDER BY \"t1\".\"username\"'), [])\n\n    def test_subquery_in_expr(self):\n        Team = Table('team')\n        Challenge = Table('challenge')\n        subq = Team.select(fn.COUNT(Team.c.id) + 1)\n        query = (Challenge\n                 .select((Challenge.c.points / subq).alias('score'))\n                 .order_by(SQL('score')))\n        self.assertSQL(query, (\n            'SELECT (\"t1\".\"points\" / ('\n            'SELECT (COUNT(\"t2\".\"id\") + ?) FROM \"team\" AS \"t2\")) AS \"score\" '\n            'FROM \"challenge\" AS \"t1\" ORDER BY score'), [1])\n\n    def test_user_defined_alias(self):\n        UA = User.alias('alt')\n        query = (User\n                 .select(User.c.id, User.c.username, UA.c.nuggz)\n                 .join(UA, on=(User.c.id == UA.c.id))\n                 .order_by(UA.c.nuggz))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\", \"alt\".\"nuggz\" '\n            'FROM \"users\" AS \"t1\" '\n            'INNER JOIN \"users\" AS \"alt\" ON (\"t1\".\"id\" = \"alt\".\"id\") '\n            'ORDER BY \"alt\".\"nuggz\"'), [])\n\n    def test_simple_cte(self):\n        cte = User.select(User.c.id).cte('user_ids')\n        query = (User\n                 .select(User.c.username)\n                 .where(User.c.id.in_(cte))\n                 .with_cte(cte))\n        self.assertSQL(query, (\n            'WITH \"user_ids\" AS (SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\") '\n            'SELECT \"t2\".\"username\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"id\" IN \"user_ids\")'), [])\n\n    def test_two_ctes(self):\n        c1 = User.select(User.c.id).cte('user_ids')\n        c2 = User.select(User.c.username).cte('user_names')\n        query = (User\n                 .select(c1.c.id, c2.c.username)\n                 .where((c1.c.id == User.c.id) &\n                        (c2.c.username == User.c.username))\n                 .with_cte(c1, c2))\n        self.assertSQL(query, (\n            'WITH \"user_ids\" AS (SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\"), '\n            '\"user_names\" AS (SELECT \"t2\".\"username\" FROM \"users\" AS \"t2\") '\n            'SELECT \"user_ids\".\"id\", \"user_names\".\"username\" '\n            'FROM \"users\" AS \"t3\" '\n            'WHERE ((\"user_ids\".\"id\" = \"t3\".\"id\") AND '\n            '(\"user_names\".\"username\" = \"t3\".\"username\"))'), [])\n\n    def test_select_from_cte(self):\n        # Use the \"select_from()\" helper on the CTE object.\n        cte = User.select(User.c.username).cte('user_cte')\n        query = cte.select_from(cte.c.username).order_by(cte.c.username)\n        self.assertSQL(query, (\n            'WITH \"user_cte\" AS (SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\") '\n            'SELECT \"user_cte\".\"username\" FROM \"user_cte\" '\n            'ORDER BY \"user_cte\".\"username\"'), [])\n\n        # Test selecting from multiple CTEs, which is done manually.\n        c1 = User.select(User.c.username).where(User.c.is_admin == 1).cte('c1')\n        c2 = User.select(User.c.username).where(User.c.is_staff == 1).cte('c2')\n        query = (Select((c1, c2), (c1.c.username, c2.c.username))\n                 .with_cte(c1, c2))\n        self.assertSQL(query, (\n            'WITH \"c1\" AS ('\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"is_admin\" = ?)), '\n            '\"c2\" AS ('\n            'SELECT \"t2\".\"username\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"is_staff\" = ?)) '\n            'SELECT \"c1\".\"username\", \"c2\".\"username\" FROM \"c1\", \"c2\"'), [1, 1])\n\n    def test_cte_select_from_2(self):\n        cte = (User\n               .select(User.c.username)\n               .where(User.c.username != 'x')\n               .cte('filtered'))\n        query = cte.select_from(cte.c.username)\n        self.assertSQL(query, (\n            'WITH \"filtered\" AS ('\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" != ?)) '\n            'SELECT \"filtered\".\"username\" FROM \"filtered\"'), ['x'])\n\n    def test_cte_select_from_with_aggregate(self):\n        cte = (User\n               .select(User.c.username,\n                       fn.COUNT(Tweet.c.id).alias('tweet_ct'))\n               .join(Tweet, JOIN.LEFT_OUTER, (Tweet.c.user_id == User.c.id))\n               .group_by(User.c.username)\n               .cte('user_stats'))\n        query = (cte\n                 .select_from(cte.c.username, cte.c.tweet_ct)\n                 .where(cte.c.tweet_ct > 0))\n        self.assertSQL(query, (\n            'WITH \"user_stats\" AS ('\n            'SELECT \"t1\".\"username\", COUNT(\"t2\".\"id\") AS \"tweet_ct\" '\n            'FROM \"users\" AS \"t1\" '\n            'LEFT OUTER JOIN \"tweets\" AS \"t2\" '\n            'ON (\"t2\".\"user_id\" = \"t1\".\"id\") '\n            'GROUP BY \"t1\".\"username\") '\n            'SELECT \"user_stats\".\"username\", \"user_stats\".\"tweet_ct\" '\n            'FROM \"user_stats\" '\n            'WHERE (\"user_stats\".\"tweet_ct\" > ?)'), [0])\n\n    def test_two_ctes_with_join(self):\n        cte_a = (User\n                 .select(User.c.id, User.c.username)\n                 .cte('active_users'))\n        cte_b = (Tweet\n                 .select(Tweet.c.user_id, fn.COUNT(Tweet.c.id).alias('ct'))\n                 .group_by(Tweet.c.user_id)\n                 .cte('tweet_counts'))\n        query = (cte_a\n                 .select_from(cte_a.c.username, cte_b.c.ct)\n                 .join(cte_b, on=(cte_a.c.id == cte_b.c.user_id))\n                 .with_cte(cte_a, cte_b)\n                 .order_by(cte_b.c.ct.desc()))\n        self.assertSQL(query, (\n            'WITH \"active_users\" AS ('\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" '\n            'FROM \"users\" AS \"t1\"), '\n            '\"tweet_counts\" AS ('\n            'SELECT \"t2\".\"user_id\", COUNT(\"t2\".\"id\") AS \"ct\" '\n            'FROM \"tweets\" AS \"t2\" '\n            'GROUP BY \"t2\".\"user_id\") '\n            'SELECT \"active_users\".\"username\", \"tweet_counts\".\"ct\" '\n            'FROM \"active_users\" '\n            'INNER JOIN \"tweet_counts\" '\n            'ON (\"active_users\".\"id\" = \"tweet_counts\".\"user_id\") '\n            'ORDER BY \"tweet_counts\".\"ct\" DESC'), [])\n\n    def test_materialize_cte(self):\n        cases = (\n            (True, 'MATERIALIZED '),\n            (False, 'NOT MATERIALIZED '),\n            (None, ''))\n        for materialized, clause in cases:\n            cte = (User\n                   .select(User.c.id)\n                   .cte('user_ids', materialized=materialized))\n            query = cte.select_from(cte.c.id).where(cte.c.id < 10)\n            self.assertSQL(query, (\n                'WITH \"user_ids\" AS %s('\n                'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\") '\n                'SELECT \"user_ids\".\"id\" FROM \"user_ids\" '\n                'WHERE (\"user_ids\".\"id\" < ?)') % clause, [10])\n\n    def test_fibonacci_cte(self):\n        q1 = Select(columns=(\n            Value(1).alias('n'),\n            Value(0).alias('fib_n'),\n            Value(1).alias('next_fib_n'))).cte('fibonacci', recursive=True)\n        n = (q1.c.n + 1).alias('n')\n        rterm = Select(columns=(\n            n,\n            q1.c.next_fib_n,\n            q1.c.fib_n + q1.c.next_fib_n)).from_(q1).where(n < 10)\n\n        cases = (\n            (q1.union_all, 'UNION ALL'),\n            (q1.union, 'UNION'))\n        for method, clause in cases:\n            cte = method(rterm)\n            query = cte.select_from(cte.c.n, cte.c.fib_n)\n            self.assertSQL(query, (\n                'WITH RECURSIVE \"fibonacci\" AS ('\n                'SELECT ? AS \"n\", ? AS \"fib_n\", ? AS \"next_fib_n\" '\n                '%s '\n                'SELECT (\"fibonacci\".\"n\" + ?) AS \"n\", \"fibonacci\".\"next_fib_n\", '\n                '(\"fibonacci\".\"fib_n\" + \"fibonacci\".\"next_fib_n\") '\n                'FROM \"fibonacci\" '\n                'WHERE (\"n\" < ?)) '\n                'SELECT \"fibonacci\".\"n\", \"fibonacci\".\"fib_n\" '\n                'FROM \"fibonacci\"' % clause), [1, 0, 1, 1, 10])\n\n    def test_cte_with_count(self):\n        cte = User.select(User.c.id).cte('user_ids')\n        query = (User\n                 .select(User.c.username)\n                 .join(cte, on=(User.c.id == cte.c.id))\n                 .with_cte(cte))\n        count = Select([query], [fn.COUNT(SQL('1'))])\n        self.assertSQL(count, (\n            'SELECT COUNT(1) FROM ('\n            'WITH \"user_ids\" AS (SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\") '\n            'SELECT \"t2\".\"username\" FROM \"users\" AS \"t2\" '\n            'INNER JOIN \"user_ids\" ON (\"t2\".\"id\" = \"user_ids\".\"id\")) '\n            'AS \"t3\"'), [])\n\n    def test_cte_subquery_in_expression(self):\n        Order = Table('order', ('id', 'description'))\n        Item = Table('item', ('id', 'order_id', 'description'))\n\n        cte = Order.select(fn.MAX(Order.id).alias('max_id')).cte('max_order')\n        qexpr = (Order\n                 .select(Order.id)\n                 .join(cte, on=(Order.id == cte.c.max_id))\n                 .with_cte(cte))\n        query = (Item\n                 .select(Item.id, Item.order_id, Item.description)\n                 .where(Item.order_id.in_(qexpr)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"order_id\", \"t1\".\"description\" '\n            'FROM \"item\" AS \"t1\" '\n            'WHERE (\"t1\".\"order_id\" IN ('\n            'WITH \"max_order\" AS ('\n            'SELECT MAX(\"t2\".\"id\") AS \"max_id\" FROM \"order\" AS \"t2\") '\n            'SELECT \"t3\".\"id\" '\n            'FROM \"order\" AS \"t3\" '\n            'INNER JOIN \"max_order\" '\n            'ON (\"t3\".\"id\" = \"max_order\".\"max_id\")))'), [])\n\n    def test_multi_update_cte(self):\n        data = [(i, 'u%sx' % i) for i in range(1, 3)]\n        vl = ValuesList(data)\n        cte = vl.select().cte('uv', columns=('id', 'username'))\n        subq = cte.select(cte.c.username).where(cte.c.id == User.c.id)\n        query = (User\n                 .update(username=subq)\n                 .where(User.c.id.in_(cte.select(cte.c.id)))\n                 .with_cte(cte))\n        self.assertSQL(query, (\n            'WITH \"uv\" (\"id\", \"username\") AS ('\n            'SELECT * FROM (VALUES (?, ?), (?, ?)) AS \"t1\") '\n            'UPDATE \"users\" SET \"username\" = ('\n            'SELECT \"uv\".\"username\" FROM \"uv\" '\n            'WHERE (\"uv\".\"id\" = \"users\".\"id\")) '\n            'WHERE (\"users\".\"id\" IN (SELECT \"uv\".\"id\" FROM \"uv\"))'),\n            [1, 'u1x', 2, 'u2x'])\n\n    def test_data_modifying_cte_delete(self):\n        Product = Table('products', ('id', 'name', 'timestamp'))\n        Archive = Table('archive', ('id', 'name', 'timestamp'))\n\n        query = (Product.delete()\n                 .where(Product.timestamp < datetime.date(2022, 1, 1))\n                 .returning(Product.id, Product.name, Product.timestamp))\n        cte = query.cte('moved_rows')\n\n        src = Select((cte,), (cte.c.id, cte.c.name, cte.c.timestamp))\n        iq = (Archive\n              .insert(src, (Archive.id, Archive.name, Archive.timestamp))\n              .with_cte(cte))\n        self.assertSQL(iq, (\n            'WITH \"moved_rows\" AS ('\n            'DELETE FROM \"products\" WHERE (\"products\".\"timestamp\" < ?) '\n            'RETURNING \"products\".\"id\", \"products\".\"name\", '\n            '\"products\".\"timestamp\") '\n            'INSERT INTO \"archive\" (\"id\", \"name\", \"timestamp\") '\n            'SELECT \"moved_rows\".\"id\", \"moved_rows\".\"name\", '\n            '\"moved_rows\".\"timestamp\" FROM \"moved_rows\"'),\n            [datetime.date(2022, 1, 1)])\n\n        Part = Table('parts', ('id', 'part', 'sub_part'))\n        base = (Part\n                .select(Part.sub_part, Part.part)\n                .where(Part.part == 'p')\n                .cte('included_parts', recursive=True,\n                     columns=('sub_part', 'part')))\n        PA = Part.alias('p')\n        recursive = (PA\n                     .select(PA.sub_part, PA.part)\n                     .join(base, on=(PA.part == base.c.sub_part)))\n        cte = base.union_all(recursive)\n\n        sq = Select((cte,), (cte.c.part,))\n        query = (Part.delete()\n                 .where(Part.part.in_(sq))\n                 .with_cte(cte))\n        self.assertSQL(query, (\n            'WITH RECURSIVE \"included_parts\" (\"sub_part\", \"part\") AS ('\n            'SELECT \"t1\".\"sub_part\", \"t1\".\"part\" FROM \"parts\" AS \"t1\" '\n            'WHERE (\"t1\".\"part\" = ?) '\n            'UNION ALL '\n            'SELECT \"p\".\"sub_part\", \"p\".\"part\" '\n            'FROM \"parts\" AS \"p\" '\n            'INNER JOIN \"included_parts\" '\n            'ON (\"p\".\"part\" = \"included_parts\".\"sub_part\")) '\n            'DELETE FROM \"parts\" '\n            'WHERE (\"parts\".\"part\" IN ('\n            'SELECT \"included_parts\".\"part\" FROM \"included_parts\"))'), ['p'])\n\n    def test_data_modifying_cte_update(self):\n        Product = Table('products', ('id', 'name', 'price'))\n        Archive = Table('archive', ('id', 'name', 'price'))\n\n        query = (Product\n                 .update(price=Product.price * 1.05)\n                 .returning(Product.id, Product.name, Product.price))\n        cte = query.cte('t')\n\n        sq = cte.select_from(cte.c.id, cte.c.name, cte.c.price)\n        self.assertSQL(sq, (\n            'WITH \"t\" AS ('\n            'UPDATE \"products\" SET \"price\" = (\"products\".\"price\" * ?) '\n            'RETURNING \"products\".\"id\", \"products\".\"name\", \"products\".\"price\")'\n            ' SELECT \"t\".\"id\", \"t\".\"name\", \"t\".\"price\" FROM \"t\"'), [1.05])\n\n        sq = Select((cte,), (cte.c.id, cte.c.price))\n        uq = (Archive\n              .update(price=sq.c.price)\n              .from_(sq)\n              .where(Archive.id == sq.c.id)\n              .with_cte(cte))\n        self.assertSQL(uq, (\n            'WITH \"t\" AS ('\n            'UPDATE \"products\" SET \"price\" = (\"products\".\"price\" * ?) '\n            'RETURNING \"products\".\"id\", \"products\".\"name\", \"products\".\"price\")'\n            ' UPDATE \"archive\" SET \"price\" = \"t1\".\"price\"'\n            ' FROM (SELECT \"t\".\"id\", \"t\".\"price\" FROM \"t\") AS \"t1\"'\n            ' WHERE (\"archive\".\"id\" = \"t1\".\"id\")'), [1.05])\n\n    def test_data_modifying_cte_insert(self):\n        Product = Table('products', ('id', 'name', 'price'))\n        Archive = Table('archive', ('id', 'name', 'price'))\n\n        query = (Product\n                 .insert({'name': 'p1', 'price': 10})\n                 .returning(Product.id, Product.name, Product.price))\n        cte = query.cte('t')\n\n        sq = cte.select_from(cte.c.id, cte.c.name, cte.c.price)\n        self.assertSQL(sq, (\n            'WITH \"t\" AS ('\n            'INSERT INTO \"products\" (\"name\", \"price\") VALUES (?, ?) '\n            'RETURNING \"products\".\"id\", \"products\".\"name\", \"products\".\"price\")'\n            ' SELECT \"t\".\"id\", \"t\".\"name\", \"t\".\"price\" FROM \"t\"'),\n            ['p1', 10])\n\n        sq = Select((cte,), (cte.c.id, cte.c.name, cte.c.price))\n        iq = (Archive\n              .insert(sq, (sq.c.id, sq.c.name, sq.c.price))\n              .with_cte(cte))\n        self.assertSQL(iq, (\n            'WITH \"t\" AS ('\n            'INSERT INTO \"products\" (\"name\", \"price\") VALUES (?, ?) '\n            'RETURNING \"products\".\"id\", \"products\".\"name\", \"products\".\"price\")'\n            ' INSERT INTO \"archive\" (\"id\", \"name\", \"price\")'\n            ' SELECT \"t\".\"id\", \"t\".\"name\", \"t\".\"price\" FROM \"t\"'), ['p1', 10])\n\n    def test_select_from_subquery(self):\n        subq = (User\n                .select(User.c.username,\n                        fn.LENGTH(User.c.username).alias('name_len'))\n                .alias('sub'))\n        query = (User\n                 .select(subq.c.username, subq.c.name_len)\n                 .from_(subq)\n                 .where(subq.c.name_len > 3)\n                 .order_by(subq.c.name_len))\n        self.assertSQL(query, (\n            'SELECT \"sub\".\"username\", \"sub\".\"name_len\" '\n            'FROM ('\n            'SELECT \"t1\".\"username\", LENGTH(\"t1\".\"username\") AS \"name_len\" '\n            'FROM \"users\" AS \"t1\") AS \"sub\" '\n            'WHERE (\"sub\".\"name_len\" > ?) '\n            'ORDER BY \"sub\".\"name_len\"'), [3])\n\n    def test_complex_select(self):\n        Order = Table('orders', columns=(\n            'region',\n            'amount',\n            'product',\n            'quantity'))\n\n        regional_sales = (Order\n                          .select(\n                              Order.region,\n                              fn.SUM(Order.amount).alias('total_sales'))\n                          .group_by(Order.region)\n                          .cte('regional_sales'))\n\n        top_regions = (regional_sales\n                       .select(regional_sales.c.region)\n                       .where(regional_sales.c.total_sales > (\n                           regional_sales.select(\n                               fn.SUM(regional_sales.c.total_sales) / 10)))\n                       .cte('top_regions'))\n\n        query = (Order\n                 .select(\n                     Order.region,\n                     Order.product,\n                     fn.SUM(Order.quantity).alias('product_units'),\n                     fn.SUM(Order.amount).alias('product_sales'))\n                 .where(\n                     Order.region << top_regions.select(top_regions.c.region))\n                 .group_by(Order.region, Order.product)\n                 .with_cte(regional_sales, top_regions))\n\n        self.assertSQL(query, (\n            'WITH \"regional_sales\" AS ('\n            'SELECT \"t1\".\"region\", SUM(\"t1\".\"amount\") AS \"total_sales\" '\n            'FROM \"orders\" AS \"t1\" '\n            'GROUP BY \"t1\".\"region\"'\n            '), '\n            '\"top_regions\" AS ('\n            'SELECT \"regional_sales\".\"region\" '\n            'FROM \"regional_sales\" '\n            'WHERE (\"regional_sales\".\"total_sales\" > '\n            '(SELECT (SUM(\"regional_sales\".\"total_sales\") / ?) '\n            'FROM \"regional_sales\"))'\n            ') '\n            'SELECT \"t2\".\"region\", \"t2\".\"product\", '\n            'SUM(\"t2\".\"quantity\") AS \"product_units\", '\n            'SUM(\"t2\".\"amount\") AS \"product_sales\" '\n            'FROM \"orders\" AS \"t2\" '\n            'WHERE ('\n            '\"t2\".\"region\" IN ('\n            'SELECT \"top_regions\".\"region\" '\n            'FROM \"top_regions\")'\n            ') GROUP BY \"t2\".\"region\", \"t2\".\"product\"'), [10])\n\n    def test_compound_select(self):\n        lhs = User.select(User.c.id).where(User.c.username == 'charlie')\n        rhs = User.select(User.c.username).where(User.c.admin == True)\n        q2 = (lhs | rhs)\n        UA = User.alias('U2')\n        q3 = q2 | UA.select(UA.c.id).where(UA.c.superuser == False)\n\n        self.assertSQL(q3, (\n            'SELECT \"t1\".\"id\" '\n            'FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" = ?) '\n            'UNION '\n            'SELECT \"t2\".\"username\" '\n            'FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"admin\" = ?) '\n            'UNION '\n            'SELECT \"U2\".\"id\" '\n            'FROM \"users\" AS \"U2\" '\n            'WHERE (\"U2\".\"superuser\" = ?)'), ['charlie', True, False])\n\n    def test_compound_operations(self):\n        admin = (User\n                 .select(User.c.username, Value('admin').alias('role'))\n                 .where(User.c.is_admin == True))\n        editors = (User\n                   .select(User.c.username, Value('editor').alias('role'))\n                   .where(User.c.is_editor == True))\n\n        union = admin.union(editors)\n        self.assertSQL(union, (\n            'SELECT \"t1\".\"username\", ? AS \"role\" '\n            'FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"is_admin\" = ?) '\n            'UNION '\n            'SELECT \"t2\".\"username\", ? AS \"role\" '\n            'FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"is_editor\" = ?)'), ['admin', 1, 'editor', 1])\n\n        xcept = editors.except_(admin)\n        self.assertSQL(xcept, (\n            'SELECT \"t1\".\"username\", ? AS \"role\" '\n            'FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"is_editor\" = ?) '\n            'EXCEPT '\n            'SELECT \"t2\".\"username\", ? AS \"role\" '\n            'FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"is_admin\" = ?)'), ['editor', 1, 'admin', 1])\n\n    def test_compound_parentheses_handling(self):\n        admin = (User\n                 .select(User.c.username, Value('admin').alias('role'))\n                 .where(User.c.is_admin == True)\n                 .order_by(User.c.id.desc())\n                 .limit(3))\n        editors = (User\n                   .select(User.c.username, Value('editor').alias('role'))\n                   .where(User.c.is_editor == True)\n                   .order_by(User.c.id.desc())\n                   .limit(5))\n\n        self.assertSQL((admin | editors), (\n            '(SELECT \"t1\".\"username\", ? AS \"role\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"is_admin\" = ?) ORDER BY \"t1\".\"id\" DESC LIMIT ?) '\n            'UNION '\n            '(SELECT \"t2\".\"username\", ? AS \"role\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"is_editor\" = ?) ORDER BY \"t2\".\"id\" DESC LIMIT ?)'),\n            ['admin', 1, 3, 'editor', 1, 5], compound_select_parentheses=True)\n\n        Reg = Table('register', ('value',))\n        lhs = Reg.select().where(Reg.value < 2)\n        rhs = Reg.select().where(Reg.value > 7)\n        compound = lhs | rhs\n\n        for csq_setting in (1, 2):\n            self.assertSQL(compound, (\n                '(SELECT \"t1\".\"value\" FROM \"register\" AS \"t1\" '\n                'WHERE (\"t1\".\"value\" < ?)) '\n                'UNION '\n                '(SELECT \"t2\".\"value\" FROM \"register\" AS \"t2\" '\n                'WHERE (\"t2\".\"value\" > ?))'),\n                [2, 7], compound_select_parentheses=csq_setting)\n\n        rhs2 = Reg.select().where(Reg.value == 5)\n        c2 = compound | rhs2\n\n        # CSQ = always, we get nested parentheses.\n        self.assertSQL(c2, (\n            '((SELECT \"t1\".\"value\" FROM \"register\" AS \"t1\" '\n            'WHERE (\"t1\".\"value\" < ?)) '\n            'UNION '\n            '(SELECT \"t2\".\"value\" FROM \"register\" AS \"t2\" '\n            'WHERE (\"t2\".\"value\" > ?))) '\n            'UNION '\n            '(SELECT \"t3\".\"value\" FROM \"register\" AS \"t3\" '\n            'WHERE (\"t3\".\"value\" = ?))'),\n            [2, 7, 5], compound_select_parentheses=1)  # Always.\n\n        # CSQ = unnested, no nesting but all individual queries have parens.\n        self.assertSQL(c2, (\n            '(SELECT \"t1\".\"value\" FROM \"register\" AS \"t1\" '\n            'WHERE (\"t1\".\"value\" < ?)) '\n            'UNION '\n            '(SELECT \"t2\".\"value\" FROM \"register\" AS \"t2\" '\n            'WHERE (\"t2\".\"value\" > ?)) '\n            'UNION '\n            '(SELECT \"t3\".\"value\" FROM \"register\" AS \"t3\" '\n            'WHERE (\"t3\".\"value\" = ?))'),\n            [2, 7, 5], compound_select_parentheses=2)  # Un-nested.\n\n    def test_compound_select_order_limit(self):\n        A = Table('a', ('col_a',))\n        B = Table('b', ('col_b',))\n        C = Table('c', ('col_c',))\n        q1 = A.select(A.col_a.alias('foo'))\n        q2 = B.select(B.col_b.alias('foo'))\n        q3 = C.select(C.col_c.alias('foo'))\n        qc = (q1 | q2 | q3)\n        qc = qc.order_by(qc.c.foo.desc()).limit(3)\n\n        self.assertSQL(qc, (\n            'SELECT \"t1\".\"col_a\" AS \"foo\" FROM \"a\" AS \"t1\" UNION '\n            'SELECT \"t2\".\"col_b\" AS \"foo\" FROM \"b\" AS \"t2\" UNION '\n            'SELECT \"t3\".\"col_c\" AS \"foo\" FROM \"c\" AS \"t3\" '\n            'ORDER BY \"foo\" DESC LIMIT ?'), [3])\n\n        self.assertSQL(qc, (\n            '((SELECT \"t1\".\"col_a\" AS \"foo\" FROM \"a\" AS \"t1\") UNION '\n            '(SELECT \"t2\".\"col_b\" AS \"foo\" FROM \"b\" AS \"t2\")) UNION '\n            '(SELECT \"t3\".\"col_c\" AS \"foo\" FROM \"c\" AS \"t3\") '\n            'ORDER BY \"foo\" DESC LIMIT ?'),\n            [3], compound_select_parentheses=1)\n\n    def test_compound_select_as_subquery(self):\n        A = Table('a', ('col_a',))\n        B = Table('b', ('col_b',))\n        q1 = A.select(A.col_a.alias('foo'))\n        q2 = B.select(B.col_b.alias('foo'))\n        union = q1 | q2\n\n        # Create an outer query and do grouping.\n        outer = (union\n                 .select_from(union.c.foo, fn.COUNT(union.c.foo).alias('ct'))\n                 .group_by(union.c.foo))\n        self.assertSQL(outer, (\n            'SELECT \"t1\".\"foo\", COUNT(\"t1\".\"foo\") AS \"ct\" FROM ('\n            'SELECT \"t2\".\"col_a\" AS \"foo\" FROM \"a\" AS \"t2\" UNION '\n            'SELECT \"t3\".\"col_b\" AS \"foo\" FROM \"b\" AS \"t3\") AS \"t1\" '\n            'GROUP BY \"t1\".\"foo\"'), [])\n\n    def test_union_with_order_and_limit(self):\n        q1 = User.select(User.c.username).where(User.c.id < 5)\n        q2 = User.select(User.c.username).where(User.c.id > 95)\n        combined = (q1 | q2).order_by(SQL('1')).limit(10)\n        self.assertSQL(combined, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" < ?) '\n            'UNION '\n            'SELECT \"t2\".\"username\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"id\" > ?) '\n            'ORDER BY 1 LIMIT ?'), [5, 95, 10])\n\n    def test_intersect(self):\n        q1 = User.select(User.c.username).where(User.c.id < 10)\n        q2 = User.select(User.c.username).where(User.c.id > 5)\n        combined = q1 & q2\n        self.assertSQL(combined, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" < ?) '\n            'INTERSECT '\n            'SELECT \"t2\".\"username\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"id\" > ?)'), [10, 5])\n\n    def test_except(self):\n        q1 = User.select(User.c.username)\n        q2 = User.select(User.c.username).where(User.c.id > 5)\n        combined = q1 - q2\n        self.assertSQL(combined, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'EXCEPT '\n            'SELECT \"t2\".\"username\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"id\" > ?)'), [5])\n\n    def test_coalesce(self):\n        Sample = Table('sample', ('counter', 'value'))\n        query = (Sample\n                 .select(fn.COALESCE(Sample.value, 0).alias('val'))\n                 .where(Sample.counter == 1))\n        self.assertSQL(query, (\n            'SELECT COALESCE(\"t1\".\"value\", ?) AS \"val\" '\n            'FROM \"sample\" AS \"t1\" '\n            'WHERE (\"t1\".\"counter\" = ?)'), [0, 1])\n\n    def test_nullif(self):\n        Sample = Table('sample', ('counter', 'value'))\n        query = (Sample\n                 .select(fn.NULLIF(Sample.value, 0).alias('val')))\n        self.assertSQL(query, (\n            'SELECT NULLIF(\"t1\".\"value\", ?) AS \"val\" '\n            'FROM \"sample\" AS \"t1\"'), [0])\n\n    def test_join_on_query(self):\n        inner = User.select(User.c.id).alias('j1')\n        query = (Tweet\n                 .select(Tweet.c.content)\n                 .join(inner, on=(Tweet.c.user_id == inner.c.id)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"content\" FROM \"tweets\" AS \"t1\" '\n            'INNER JOIN (SELECT \"t2\".\"id\" FROM \"users\" AS \"t2\") AS \"j1\" '\n            'ON (\"t1\".\"user_id\" = \"j1\".\"id\")'), [])\n\n    def test_join_on_misc(self):\n        cond = fn.Magic(Person.id, Note.id).alias('magic')\n        query = Person.select(Person.id).join(Note, on=cond)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" '\n            'INNER JOIN \"note\" AS \"t2\" '\n            'ON Magic(\"t1\".\"id\", \"t2\".\"id\") AS \"magic\"'), [])\n\n    def test_lateral_subquery_model(self):\n        inner = (Tweet\n                 .select(Tweet.c.content)\n                 .where(Tweet.c.user_id == User.c.id)\n                 .order_by(Tweet.c.timestamp.desc())\n                 .limit(1))\n        query = (User\n                 .select(User.c.username, inner.c.content)\n                 .join(inner, JOIN.LEFT_LATERAL, on=True))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\", \"t2\".\"content\" '\n            'FROM \"users\" AS \"t1\" '\n            'LEFT JOIN LATERAL ('\n            'SELECT \"t3\".\"content\" FROM \"tweets\" AS \"t3\" '\n            'WHERE (\"t3\".\"user_id\" = \"t1\".\"id\") '\n            'ORDER BY \"t3\".\"timestamp\" DESC LIMIT ?) AS \"t2\" ON ?'),\n            [1, True])\n\n    def test_all_clauses(self):\n        count = fn.COUNT(Tweet.c.id).alias('ct')\n        query = (User\n                 .select(User.c.username, count)\n                 .join(Tweet, JOIN.LEFT_OUTER,\n                       on=(User.c.id == Tweet.c.user_id))\n                 .where(User.c.is_admin == 1)\n                 .group_by(User.c.username)\n                 .having(count > 10)\n                 .order_by(count.desc()))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\", COUNT(\"t2\".\"id\") AS \"ct\" '\n            'FROM \"users\" AS \"t1\" '\n            'LEFT OUTER JOIN \"tweets\" AS \"t2\" '\n            'ON (\"t1\".\"id\" = \"t2\".\"user_id\") '\n            'WHERE (\"t1\".\"is_admin\" = ?) '\n            'GROUP BY \"t1\".\"username\" '\n            'HAVING (\"ct\" > ?) '\n            'ORDER BY \"ct\" DESC'), [1, 10])\n\n    def test_order_by_collate(self):\n        query = (User\n                 .select(User.c.username)\n                 .order_by(User.c.username.asc(collation='binary')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'ORDER BY \"t1\".\"username\" ASC COLLATE binary'), [])\n\n    def test_order_by_nulls(self):\n        query = (User\n                 .select(User.c.username)\n                 .order_by(User.c.ts.desc(nulls='LAST')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'ORDER BY \"t1\".\"ts\" DESC NULLS LAST'), [], nulls_ordering=True)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'ORDER BY CASE WHEN (\"t1\".\"ts\" IS NULL) THEN ? ELSE ? END, '\n            '\"t1\".\"ts\" DESC'), [1, 0], nulls_ordering=False)\n\n        query = (User\n                 .select(User.c.username)\n                 .order_by(User.c.ts.desc(nulls='first')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'ORDER BY \"t1\".\"ts\" DESC NULLS first'), [], nulls_ordering=True)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'ORDER BY CASE WHEN (\"t1\".\"ts\" IS NULL) THEN ? ELSE ? END, '\n            '\"t1\".\"ts\" DESC'), [0, 1], nulls_ordering=False)\n\n    def test_in_value_representation(self):\n        query = (User\n                 .select(User.c.id)\n                 .where(User.c.username.in_(['foo', 'bar', 'baz'])))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" IN (?, ?, ?))'), ['foo', 'bar', 'baz'])\n\n    def test_tuple_comparison(self):\n        name_dob = Tuple(Person.name, Person.dob)\n        query = (Person\n                 .select(Person.id)\n                 .where(name_dob == ('foo', '2017-01-01')))\n        expected = ('SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" '\n                    'WHERE ((\"t1\".\"name\", \"t1\".\"dob\") = (?, ?))')\n        self.assertSQL(query, expected, ['foo', '2017-01-01'])\n\n        # Also works specifying rhs values as Tuple().\n        query = (Person\n                 .select(Person.id)\n                 .where(name_dob == Tuple('foo', '2017-01-01')))\n        self.assertSQL(query, expected, ['foo', '2017-01-01'])\n\n    def test_tuple_comparison_subquery(self):\n        PA = Person.alias('pa')\n        subquery = (PA\n                    .select(PA.name, PA.id)\n                    .where(PA.name != 'huey'))\n\n        query = (Person\n                 .select(Person.name)\n                 .where(Tuple(Person.name, Person.id).in_(subquery)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\" FROM \"person\" AS \"t1\" '\n            'WHERE ((\"t1\".\"name\", \"t1\".\"id\") IN ('\n            'SELECT \"pa\".\"name\", \"pa\".\"id\" FROM \"person\" AS \"pa\" '\n            'WHERE (\"pa\".\"name\" != ?)))'), ['huey'])\n\n    def test_tuple_in_subquery(self):\n        subq = (Tweet\n                .select(Tweet.c.user_id, Tweet.c.content)\n                .where(Tweet.c.content == 'special'))\n        query = (User\n                 .select(User.c.id, User.c.username)\n                 .where(Tuple(User.c.id, User.c.username).in_(subq)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"username\" '\n            'FROM \"users\" AS \"t1\" '\n            'WHERE ((\"t1\".\"id\", \"t1\".\"username\") IN ('\n            'SELECT \"t2\".\"user_id\", \"t2\".\"content\" '\n            'FROM \"tweets\" AS \"t2\" '\n            'WHERE (\"t2\".\"content\" = ?)))'), ['special'])\n\n    def test_empty_in(self):\n        query = User.select(User.c.id).where(User.c.username.in_([]))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (0 = 1)'), [])\n\n        query = User.select(User.c.id).where(User.c.username.not_in([]))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (1 = 1)'), [])\n\n        query = User.select(User.c.id).where(User.c.username.in_(Value([])))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (0 = 1)'), [])\n\n    def test_add_custom_op(self):\n        def mod(lhs, rhs):\n            return Expression(lhs, '%', rhs)\n\n        Stat = Table('stats')\n        query = (Stat\n                 .select(fn.COUNT(Stat.c.id))\n                 .where(mod(Stat.c.index, 10) == 0))\n        self.assertSQL(query, (\n            'SELECT COUNT(\"t1\".\"id\") FROM \"stats\" AS \"t1\" '\n            'WHERE ((\"t1\".\"index\" % ?) = ?)'), [10, 0])\n\n    def test_where_convert_to_is_null(self):\n        Note = Table('notes', ('id', 'content', 'user_id'))\n        query = Note.select().where(Note.user_id == None)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"content\", \"t1\".\"user_id\" '\n            'FROM \"notes\" AS \"t1\" WHERE (\"t1\".\"user_id\" IS NULL)'), [])\n\n    def test_like_escape(self):\n        T = Table('tbl', ('key',))\n        def assertLike(expr, expected):\n            query = T.select().where(expr)\n            sql, params = __sql__(T.select().where(expr))\n            match_obj = re.search(r'\\(\"t1\".\"key\" (ILIKE[^\\)]+)\\)', sql)\n            if match_obj is None:\n                raise AssertionError('LIKE expression not found in query.')\n            like, = match_obj.groups()\n            self.assertEqual((like, params), expected)\n\n        cases = (\n            (T.key.contains('base'), ('ILIKE ?', ['%base%'])),\n            (T.key.contains('x_y'), (\"ILIKE ? ESCAPE ?\", ['%x\\\\_y%', '\\\\'])),\n            (T.key.contains('__y'), (\"ILIKE ? ESCAPE ?\", ['%\\\\_\\\\_y%', '\\\\'])),\n            (T.key.contains('%'), (\"ILIKE ? ESCAPE ?\", ['%\\\\%%', '\\\\'])),\n            (T.key.contains('_%'), (\"ILIKE ? ESCAPE ?\", ['%\\\\_\\\\%%', '\\\\'])),\n            (T.key.startswith('base'), (\"ILIKE ?\", ['base%'])),\n            (T.key.startswith('x_y'), (\"ILIKE ? ESCAPE ?\", ['x\\\\_y%', '\\\\'])),\n            (T.key.startswith('x%'), (\"ILIKE ? ESCAPE ?\", ['x\\\\%%', '\\\\'])),\n            (T.key.startswith('_%'), (\"ILIKE ? ESCAPE ?\", ['\\\\_\\\\%%', '\\\\'])),\n            (T.key.endswith('base'), (\"ILIKE ?\", ['%base'])),\n            (T.key.endswith('x_y'), (\"ILIKE ? ESCAPE ?\", ['%x\\\\_y', '\\\\'])),\n            (T.key.endswith('x%'), (\"ILIKE ? ESCAPE ?\", ['%x\\\\%', '\\\\'])),\n            (T.key.endswith('_%'), (\"ILIKE ? ESCAPE ?\", ['%\\\\_\\\\%', '\\\\'])),\n        )\n\n        for expr, expected in cases:\n            assertLike(expr, expected)\n\n    def test_like_expr(self):\n        query = User.select(User.c.id).where(User.c.username.like('%foo%'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" LIKE ?)'), ['%foo%'])\n\n        query = User.select(User.c.id).where(User.c.username.ilike('%foo%'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" ILIKE ?)'), ['%foo%'])\n\n    def test_field_ops(self):\n        query = User.select(User.c.id).where(User.c.username.regexp('[a-z]+'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" REGEXP ?)'), ['[a-z]+'])\n\n        query = User.select(User.c.id).where(User.c.username.contains('abc'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"username\" ILIKE ?)'), ['%abc%'])\n\n    def test_bitwise_ops(self):\n        query = User.select(User.c.id).where(User.c.id.bin_and(4))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" & ?)'), [4])\n\n        query = User.select(User.c.id).where(User.c.id.bin_or(1))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"id\" | ?)'), [1])\n\n    def test_entity_escaping(self):\n        Tbl = Table('te\"st')\n        query = Tbl.select(Tbl.c.id).where(Tbl.c.value > 5)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"te\"\"st\" AS \"t1\" '\n            'WHERE (\"t1\".\"value\" > ?)'), [5])\n\n        self.assertSQL(query, (\n            'SELECT `t1`.`id` FROM `te\"st` AS `t1` '\n            'WHERE (`t1`.`value` > ?)'), [5], quote='``')\n\n\nclass TestInsertQuery(BaseTestCase):\n    def test_insert_simple(self):\n        query = User.insert({\n            User.c.username: 'charlie',\n            User.c.superuser: False,\n            User.c.admin: True})\n        self.assertSQL(query, (\n            'INSERT INTO \"users\" (\"admin\", \"superuser\", \"username\") '\n            'VALUES (?, ?, ?)'), [True, False, 'charlie'])\n\n    @requires_sqlite\n    def test_replace_sqlite(self):\n        query = User.replace({\n            User.c.username: 'charlie',\n            User.c.superuser: False})\n        self.assertSQL(query, (\n            'INSERT OR REPLACE INTO \"users\" (\"superuser\", \"username\") '\n            'VALUES (?, ?)'), [False, 'charlie'])\n\n    @requires_mysql\n    def test_replace_mysql(self):\n        query = User.replace({\n            User.c.username: 'charlie',\n            User.c.superuser: False})\n        self.assertSQL(query, (\n            'REPLACE INTO \"users\" (\"superuser\", \"username\") '\n            'VALUES (?, ?)'), [False, 'charlie'])\n\n    def test_insert_list(self):\n        data = [\n            {Person.name: 'charlie'},\n            {Person.name: 'huey'},\n            {Person.name: 'zaizee'}]\n        query = Person.insert(data)\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?), (?), (?)'),\n            ['charlie', 'huey', 'zaizee'])\n\n    def test_insert_list_with_columns(self):\n        data = [(i,) for i in ('charlie', 'huey', 'zaizee')]\n        query = Person.insert(data, columns=[Person.name])\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?), (?), (?)'),\n            ['charlie', 'huey', 'zaizee'])\n\n        # Use column name instead of column instance.\n        query = Person.insert(data, columns=['name'])\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?), (?), (?)'),\n            ['charlie', 'huey', 'zaizee'])\n\n    def test_insert_list_infer_columns(self):\n        data = [('p1', '1980-01-01'), ('p2', '1980-02-02')]\n        self.assertSQL(Person.insert(data), (\n            'INSERT INTO \"person\" (\"name\", \"dob\") VALUES (?, ?), (?, ?)'),\n            ['p1', '1980-01-01', 'p2', '1980-02-02'])\n\n        # Cannot infer any columns for User.\n        data = [('u1',), ('u2',)]\n        self.assertRaises(ValueError, User.insert(data).sql)\n\n        # Note declares columns, but no primary key. So we would have to\n        # include it for this to work.\n        data = [(1, 'p1-n'), (2, 'p2-n')]\n        self.assertRaises(ValueError, Note.insert(data).sql)\n\n        data = [(1, 1, 'p1-n'), (2, 2, 'p2-n')]\n        self.assertSQL(Note.insert(data), (\n            'INSERT INTO \"note\" (\"id\", \"person_id\", \"content\") '\n            'VALUES (?, ?, ?), (?, ?, ?)'), [1, 1, 'p1-n', 2, 2, 'p2-n'])\n\n    def test_insert_query(self):\n        source = User.select(User.c.username).where(User.c.admin == False)\n        query = Person.insert(source, columns=[Person.name])\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") '\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\" '\n            'WHERE (\"t1\".\"admin\" = ?)'), [False])\n\n    def test_insert_query_cte(self):\n        cte = User.select(User.c.username).cte('foo')\n        source = cte.select(cte.c.username)\n        query = Person.insert(source, columns=[Person.name]).with_cte(cte)\n        self.assertSQL(query, (\n            'WITH \"foo\" AS (SELECT \"t1\".\"username\" FROM \"users\" AS \"t1\") '\n            'INSERT INTO \"person\" (\"name\") '\n            'SELECT \"foo\".\"username\" FROM \"foo\"'), [])\n\n    def test_insert_single_value_query(self):\n        query = Person.select(Person.id).where(Person.name == 'huey')\n        insert = Note.insert({\n            Note.person_id: query,\n            Note.content: 'hello'})\n        self.assertSQL(insert, (\n            'INSERT INTO \"note\" (\"content\", \"person_id\") VALUES (?, '\n            '(SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = ?)))'), ['hello', 'huey'])\n\n    def test_insert_returning(self):\n        query = (Person\n                 .insert({\n                     Person.name: 'zaizee',\n                     Person.dob: datetime.date(2000, 1, 2)})\n                 .returning(Person.id, Person.name, Person.dob))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") '\n            'VALUES (?, ?) '\n            'RETURNING \"person\".\"id\", \"person\".\"name\", \"person\".\"dob\"'),\n            [datetime.date(2000, 1, 2), 'zaizee'])\n\n        query = query.returning(Person.id, Person.name.alias('new_name'))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") '\n            'VALUES (?, ?) '\n            'RETURNING \"person\".\"id\", \"person\".\"name\" AS \"new_name\"'),\n            [datetime.date(2000, 1, 2), 'zaizee'])\n\n    def test_insert_returning_expression(self):\n        query = (Person\n                 .insert(name='huey')\n                 .returning(Person.id, Person.name,\n                            fn.LENGTH(Person.name).alias('ulen')))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?) '\n            'RETURNING \"person\".\"id\", '\n            '\"person\".\"name\", '\n            'LENGTH(\"person\".\"name\") AS \"ulen\"'), ['huey'])\n\n    def test_empty(self):\n        class Empty(TestModel): pass\n        query = Empty.insert()\n        if isinstance(db, MySQLDatabase):\n            sql = 'INSERT INTO \"empty\" () VALUES ()'\n        elif isinstance(db, PostgresqlDatabase):\n            sql = 'INSERT INTO \"empty\" DEFAULT VALUES RETURNING \"empty\".\"id\"'\n        else:\n            sql = 'INSERT INTO \"empty\" DEFAULT VALUES'\n        self.assertSQL(query, sql, [])\n\n\nclass TestUpdateQuery(BaseTestCase):\n    def test_update_query(self):\n        query = (User\n                 .update({\n                     User.c.username: 'nuggie',\n                     User.c.admin: False,\n                     User.c.counter: User.c.counter + 1})\n                 .where(User.c.username == 'nugz'))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET '\n            '\"admin\" = ?, '\n            '\"counter\" = (\"users\".\"counter\" + ?), '\n            '\"username\" = ? '\n            'WHERE (\"users\".\"username\" = ?)'), [False, 1, 'nuggie', 'nugz'])\n\n    def test_update_subquery(self):\n        count = fn.COUNT(Tweet.c.id).alias('ct')\n        subquery = (User\n                    .select(User.c.id, count)\n                    .join(Tweet, on=(Tweet.c.user_id == User.c.id))\n                    .group_by(User.c.id)\n                    .having(count > 100))\n        query = (User\n                 .update({\n                     User.c.muted: True,\n                     User.c.counter: 0})\n                 .where(User.c.id << subquery))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET '\n            '\"counter\" = ?, '\n            '\"muted\" = ? '\n            'WHERE (\"users\".\"id\" IN ('\n            'SELECT \"users\".\"id\", COUNT(\"t1\".\"id\") AS \"ct\" '\n            'FROM \"users\" AS \"users\" '\n            'INNER JOIN \"tweets\" AS \"t1\" '\n            'ON (\"t1\".\"user_id\" = \"users\".\"id\") '\n            'GROUP BY \"users\".\"id\" '\n            'HAVING (\"ct\" > ?)))'), [0, True, 100])\n\n    def test_update_value_subquery(self):\n        subquery = (Tweet\n                    .select(fn.MAX(Tweet.c.id))\n                    .where(Tweet.c.user_id == User.c.id))\n        query = (User\n                 .update({User.c.last_tweet_id: subquery})\n                 .where(User.c.last_tweet_id.is_null(True)))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET '\n            '\"last_tweet_id\" = (SELECT MAX(\"t1\".\"id\") FROM \"tweets\" AS \"t1\" '\n            'WHERE (\"t1\".\"user_id\" = \"users\".\"id\")) '\n            'WHERE (\"users\".\"last_tweet_id\" IS NULL)'), [])\n\n    def test_update_from(self):\n        data = [(1, 'u1-x'), (2, 'u2-x')]\n        vl = ValuesList(data, columns=('id', 'username'), alias='tmp')\n        query = (User\n                 .update(username=vl.c.username)\n                 .from_(vl)\n                 .where(User.c.id == vl.c.id))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"username\" = \"tmp\".\"username\" '\n            'FROM (VALUES (?, ?), (?, ?)) AS \"tmp\"(\"id\", \"username\") '\n            'WHERE (\"users\".\"id\" = \"tmp\".\"id\")'), [1, 'u1-x', 2, 'u2-x'])\n\n        subq = vl.select(vl.c.id, vl.c.username)\n        query = (User\n                 .update({User.c.username: subq.c.username})\n                 .from_(subq)\n                 .where(User.c.id == subq.c.id))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"username\" = \"t1\".\"username\" FROM ('\n            'SELECT \"tmp\".\"id\", \"tmp\".\"username\" '\n            'FROM (VALUES (?, ?), (?, ?)) AS \"tmp\"(\"id\", \"username\")) AS \"t1\" '\n            'WHERE (\"users\".\"id\" = \"t1\".\"id\")'), [1, 'u1-x', 2, 'u2-x'])\n\n    def test_update_from_subquery(self):\n        subq = (Tweet\n                .select(Tweet.c.user_id,\n                        fn.COUNT(Tweet.c.id).alias('ct'))\n                .group_by(Tweet.c.user_id)\n                .alias('tweet_ct'))\n        query = (User\n                 .update({User.c.username: fn.CONCAT(\n                     User.c.username, ' (', subq.c.ct, ')')})\n                 .from_(subq)\n                 .where(User.c.id == subq.c.user_id))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"username\" = CONCAT('\n            '\"users\".\"username\", ?, \"tweet_ct\".\"ct\", ?) '\n            'FROM ('\n            'SELECT \"t1\".\"user_id\", COUNT(\"t1\".\"id\") AS \"ct\" '\n            'FROM \"tweets\" AS \"t1\" '\n            'GROUP BY \"t1\".\"user_id\") AS \"tweet_ct\" '\n            'WHERE (\"users\".\"id\" = \"tweet_ct\".\"user_id\")'),\n            [' (', ')'])\n\n    def test_update_returning(self):\n        query = (User\n                 .update({User.c.is_admin: True})\n                 .where(User.c.username == 'charlie')\n                 .returning(User.c.id))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"is_admin\" = ? WHERE (\"users\".\"username\" = ?) '\n            'RETURNING \"users\".\"id\"'), [True, 'charlie'])\n\n        query = query.returning(User.c.is_admin.alias('new_is_admin'))\n        self.assertSQL(query, (\n            'UPDATE \"users\" SET \"is_admin\" = ? WHERE (\"users\".\"username\" = ?) '\n            'RETURNING \"users\".\"is_admin\" AS \"new_is_admin\"'),\n            [True, 'charlie'])\n\n\nclass TestDeleteQuery(BaseTestCase):\n    def test_delete_query(self):\n        query = (User\n                 .delete()\n                 .where(User.c.username != 'charlie')\n                 .limit(3))\n        self.assertSQL(query, (\n            'DELETE FROM \"users\" WHERE (\"users\".\"username\" != ?) LIMIT ?'),\n            ['charlie', 3])\n\n    def test_delete_subquery(self):\n        count = fn.COUNT(Tweet.c.id).alias('ct')\n        subquery = (User\n                    .select(User.c.id, count)\n                    .join(Tweet, on=(Tweet.c.user_id == User.c.id))\n                    .group_by(User.c.id)\n                    .having(count > 100))\n        query = (User\n                 .delete()\n                 .where(User.c.id << subquery))\n        self.assertSQL(query, (\n            'DELETE FROM \"users\" '\n            'WHERE (\"users\".\"id\" IN ('\n            'SELECT \"users\".\"id\", COUNT(\"t1\".\"id\") AS \"ct\" '\n            'FROM \"users\" AS \"users\" '\n            'INNER JOIN \"tweets\" AS \"t1\" ON (\"t1\".\"user_id\" = \"users\".\"id\") '\n            'GROUP BY \"users\".\"id\" '\n            'HAVING (\"ct\" > ?)))'), [100])\n\n    def test_delete_cte(self):\n        cte = (User\n               .select(User.c.id)\n               .where(User.c.admin == True)\n               .cte('u'))\n        query = (User\n                 .delete()\n                 .where(User.c.id << cte.select(cte.c.id))\n                 .with_cte(cte))\n        self.assertSQL(query, (\n            'WITH \"u\" AS '\n            '(SELECT \"t1\".\"id\" FROM \"users\" AS \"t1\" WHERE (\"t1\".\"admin\" = ?)) '\n            'DELETE FROM \"users\" '\n            'WHERE (\"users\".\"id\" IN (SELECT \"u\".\"id\" FROM \"u\"))'), [True])\n\n    def test_delete_returning(self):\n        query = (User\n                 .delete()\n                 .where(User.c.id > 2)\n                 .returning(User.c.username))\n        self.assertSQL(query, (\n            'DELETE FROM \"users\" '\n            'WHERE (\"users\".\"id\" > ?) '\n            'RETURNING \"users\".\"username\"'), [2])\n\n        query = query.returning(User.c.id, User.c.username, SQL('1'))\n        self.assertSQL(query, (\n            'DELETE FROM \"users\" '\n            'WHERE (\"users\".\"id\" > ?) '\n            'RETURNING \"users\".\"id\", \"users\".\"username\", 1'), [2])\n\n        query = query.returning(User.c.id.alias('old_id'))\n        self.assertSQL(query, (\n            'DELETE FROM \"users\" '\n            'WHERE (\"users\".\"id\" > ?) '\n            'RETURNING \"users\".\"id\" AS \"old_id\"'), [2])\n\n\nRegister = Table('register', ('id', 'value', 'category'))\n\n\nclass TestWindowFunctions(BaseTestCase):\n    def test_partition_unordered(self):\n        partition = [Register.category]\n        query = (Register\n                 .select(\n                     Register.category,\n                     Register.value,\n                     fn.AVG(Register.value).over(partition_by=partition))\n                 .order_by(Register.id))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"category\", \"t1\".\"value\", AVG(\"t1\".\"value\") '\n            'OVER (PARTITION BY \"t1\".\"category\") '\n            'FROM \"register\" AS \"t1\" ORDER BY \"t1\".\"id\"'), [])\n\n    def test_ordered_unpartitioned(self):\n        query = (Register\n                 .select(\n                     Register.value,\n                     fn.RANK().over(order_by=[Register.value])))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", RANK() OVER (ORDER BY \"t1\".\"value\") '\n            'FROM \"register\" AS \"t1\"'), [])\n\n    def test_ordered_partitioned(self):\n        query = Register.select(\n            Register.value,\n            fn.SUM(Register.value).over(\n                order_by=Register.id,\n                partition_by=Register.category).alias('rsum'))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", SUM(\"t1\".\"value\") '\n            'OVER (PARTITION BY \"t1\".\"category\" ORDER BY \"t1\".\"id\") AS \"rsum\" '\n            'FROM \"register\" AS \"t1\"'), [])\n\n    def test_empty_over(self):\n        query = (Register\n                 .select(Register.value, fn.LAG(Register.value, 1).over())\n                 .order_by(Register.value))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", LAG(\"t1\".\"value\", ?) OVER () '\n            'FROM \"register\" AS \"t1\" '\n            'ORDER BY \"t1\".\"value\"'), [1])\n\n    def test_frame(self):\n        query = (Register\n                 .select(\n                     Register.value,\n                     fn.AVG(Register.value).over(\n                         partition_by=[Register.category],\n                         start=Window.preceding(),\n                         end=Window.following(2))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", AVG(\"t1\".\"value\") '\n            'OVER (PARTITION BY \"t1\".\"category\" '\n            'ROWS BETWEEN UNBOUNDED PRECEDING AND 2 FOLLOWING) '\n            'FROM \"register\" AS \"t1\"'), [])\n\n        query = (Register\n                 .select(Register.value, fn.AVG(Register.value).over(\n                     partition_by=[Register.category],\n                     order_by=[Register.value],\n                     start=Window.CURRENT_ROW,\n                     end=Window.following())))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", AVG(\"t1\".\"value\") '\n            'OVER (PARTITION BY \"t1\".\"category\" '\n            'ORDER BY \"t1\".\"value\" '\n            'ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) '\n            'FROM \"register\" AS \"t1\"'), [])\n\n    def test_frame_types(self):\n        def assertFrame(over_kwargs, expected):\n            query = Register.select(\n                Register.value,\n                fn.SUM(Register.value).over(**over_kwargs))\n            sql, params = __sql__(query)\n            match_obj = re.search(r'OVER \\((.*?)\\) FROM', sql)\n            self.assertTrue(match_obj is not None)\n            self.assertEqual(match_obj.groups()[0], expected)\n            self.assertEqual(params, [])\n\n        # No parameters -- empty OVER().\n        assertFrame({}, (''))\n        # Explicitly specify RANGE / ROWS frame-types.\n        assertFrame({'frame_type': Window.RANGE}, 'RANGE UNBOUNDED PRECEDING')\n        assertFrame({'frame_type': Window.ROWS}, 'ROWS UNBOUNDED PRECEDING')\n\n        # Start and end boundaries.\n        assertFrame({'start': Window.preceding(), 'end': Window.following()},\n                    'ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING')\n        assertFrame({\n            'start': Window.preceding(),\n            'end': Window.following(),\n            'frame_type': Window.RANGE,\n        }, 'RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING')\n        assertFrame({\n            'start': Window.preceding(),\n            'end': Window.following(),\n            'frame_type': Window.ROWS,\n        }, 'ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING')\n\n        # Start boundary.\n        assertFrame({'start': Window.preceding()}, 'ROWS UNBOUNDED PRECEDING')\n        assertFrame({'start': Window.preceding(), 'frame_type': Window.RANGE},\n                    'RANGE UNBOUNDED PRECEDING')\n        assertFrame({'start': Window.preceding(), 'frame_type': Window.ROWS},\n                    'ROWS UNBOUNDED PRECEDING')\n\n        # Ordered or partitioned.\n        assertFrame({'order_by': Register.value}, 'ORDER BY \"t1\".\"value\"')\n        assertFrame({'frame_type': Window.RANGE, 'order_by': Register.value},\n                    'ORDER BY \"t1\".\"value\" RANGE UNBOUNDED PRECEDING')\n        assertFrame({'frame_type': Window.ROWS, 'order_by': Register.value},\n                    'ORDER BY \"t1\".\"value\" ROWS UNBOUNDED PRECEDING')\n        assertFrame({'partition_by': Register.category},\n                    'PARTITION BY \"t1\".\"category\"')\n        assertFrame({\n            'frame_type': Window.RANGE,\n            'partition_by': Register.category,\n        }, 'PARTITION BY \"t1\".\"category\" RANGE UNBOUNDED PRECEDING')\n        assertFrame({\n            'frame_type': Window.ROWS,\n            'partition_by': Register.category,\n        }, 'PARTITION BY \"t1\".\"category\" ROWS UNBOUNDED PRECEDING')\n\n        # Ordering and boundaries.\n        assertFrame({'order_by': Register.value, 'start': Window.CURRENT_ROW,\n                     'end': Window.following()},\n                    ('ORDER BY \"t1\".\"value\" '\n                     'ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING'))\n        assertFrame({'order_by': Register.value, 'start': Window.CURRENT_ROW,\n                     'end': Window.following(), 'frame_type': Window.RANGE},\n                    ('ORDER BY \"t1\".\"value\" '\n                     'RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING'))\n        assertFrame({'order_by': Register.value, 'start': Window.CURRENT_ROW,\n                     'end': Window.following(), 'frame_type': Window.ROWS},\n                    ('ORDER BY \"t1\".\"value\" '\n                     'ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING'))\n\n    def test_running_total(self):\n        EventLog = Table('evtlog', ('id', 'timestamp', 'data'))\n\n        w = fn.SUM(EventLog.timestamp).over(order_by=[EventLog.timestamp])\n        query = (EventLog\n                 .select(EventLog.timestamp, EventLog.data, w.alias('elapsed'))\n                 .order_by(EventLog.timestamp))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"timestamp\", \"t1\".\"data\", '\n            'SUM(\"t1\".\"timestamp\") OVER (ORDER BY \"t1\".\"timestamp\") '\n            'AS \"elapsed\" '\n            'FROM \"evtlog\" AS \"t1\" ORDER BY \"t1\".\"timestamp\"'), [])\n\n        w = fn.SUM(EventLog.timestamp).over(\n            order_by=[EventLog.timestamp],\n            partition_by=[EventLog.data])\n        query = (EventLog\n                 .select(EventLog.timestamp, EventLog.data, w.alias('elapsed'))\n                 .order_by(EventLog.timestamp))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"timestamp\", \"t1\".\"data\", '\n            'SUM(\"t1\".\"timestamp\") OVER '\n            '(PARTITION BY \"t1\".\"data\" ORDER BY \"t1\".\"timestamp\") AS \"elapsed\"'\n            ' FROM \"evtlog\" AS \"t1\" ORDER BY \"t1\".\"timestamp\"'), [])\n\n    def test_named_window(self):\n        window = Window(partition_by=[Register.category])\n        query = (Register\n                 .select(\n                     Register.category,\n                     Register.value,\n                     fn.AVG(Register.value).over(window))\n                 .window(window))\n\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"category\", \"t1\".\"value\", AVG(\"t1\".\"value\") '\n            'OVER \"w\" '\n            'FROM \"register\" AS \"t1\" '\n            'WINDOW \"w\" AS (PARTITION BY \"t1\".\"category\")'), [])\n\n        window = Window(\n            partition_by=[Register.category],\n            order_by=[Register.value.desc()])\n        query = (Register\n                 .select(\n                     Register.value,\n                     fn.RANK().over(window))\n                 .window(window))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", RANK() OVER \"w\" '\n            'FROM \"register\" AS \"t1\" '\n            'WINDOW \"w\" AS ('\n            'PARTITION BY \"t1\".\"category\" '\n            'ORDER BY \"t1\".\"value\" DESC)'), [])\n\n    def test_multiple_windows(self):\n        w1 = Window(partition_by=[Register.category]).alias('w1')\n        w2 = Window(order_by=[Register.value]).alias('w2')\n        query = (Register\n                 .select(\n                     Register.value,\n                     fn.AVG(Register.value).over(w1),\n                     fn.RANK().over(w2))\n                 .window(w1, w2))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", AVG(\"t1\".\"value\") OVER \"w1\", '\n            'RANK() OVER \"w2\" '\n            'FROM \"register\" AS \"t1\" '\n            'WINDOW \"w1\" AS (PARTITION BY \"t1\".\"category\"), '\n            '\"w2\" AS (ORDER BY \"t1\".\"value\")'), [])\n\n    def test_alias_window(self):\n        w = Window(order_by=Register.value).alias('wx')\n        query = Register.select(Register.value, fn.RANK().over(w)).window(w)\n\n        # We can re-alias the window and it's updated alias is reflected\n        # correctly in the final query.\n        w.alias('wz')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"value\", RANK() OVER \"wz\" '\n            'FROM \"register\" AS \"t1\" '\n            'WINDOW \"wz\" AS (ORDER BY \"t1\".\"value\")'), [])\n\n    def test_reuse_window(self):\n        EventLog = Table('evt', ('id', 'timestamp', 'key'))\n        window = Window(partition_by=[EventLog.key],\n                        order_by=[EventLog.timestamp])\n        query = (EventLog\n                 .select(EventLog.timestamp, EventLog.key,\n                         fn.NTILE(4).over(window).alias('quartile'),\n                         fn.NTILE(5).over(window).alias('quintile'),\n                         fn.NTILE(100).over(window).alias('percentile'))\n                 .order_by(EventLog.timestamp)\n                 .window(window))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"timestamp\", \"t1\".\"key\", '\n            'NTILE(?) OVER \"w\" AS \"quartile\", '\n            'NTILE(?) OVER \"w\" AS \"quintile\", '\n            'NTILE(?) OVER \"w\" AS \"percentile\" '\n            'FROM \"evt\" AS \"t1\" '\n            'WINDOW \"w\" AS ('\n            'PARTITION BY \"t1\".\"key\" ORDER BY \"t1\".\"timestamp\") '\n            'ORDER BY \"t1\".\"timestamp\"'), [4, 5, 100])\n\n    def test_filter_clause(self):\n        condsum = fn.SUM(Register.value).filter(Register.value > 1).over(\n            order_by=[Register.id], partition_by=[Register.category],\n            start=Window.preceding(1))\n        query = (Register\n                 .select(Register.category, Register.value, condsum)\n                 .order_by(Register.category))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"category\", \"t1\".\"value\", SUM(\"t1\".\"value\") FILTER ('\n            'WHERE (\"t1\".\"value\" > ?)) OVER (PARTITION BY \"t1\".\"category\" '\n            'ORDER BY \"t1\".\"id\" ROWS 1 PRECEDING) '\n            'FROM \"register\" AS \"t1\" '\n            'ORDER BY \"t1\".\"category\"'), [1])\n\n    def test_window_in_orderby(self):\n        Register = Table('register', ['id', 'value'])\n        w = Window(partition_by=[Register.value], order_by=[Register.id])\n        query = (Register\n                 .select()\n                 .window(w)\n                 .order_by(fn.FIRST_VALUE(Register.id).over(w)))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"value\" FROM \"register\" AS \"t1\" '\n            'WINDOW \"w\" AS (PARTITION BY \"t1\".\"value\" ORDER BY \"t1\".\"id\") '\n            'ORDER BY FIRST_VALUE(\"t1\".\"id\") OVER \"w\"'), [])\n\n        fv = fn.FIRST_VALUE(Register.id).over(\n            partition_by=[Register.value],\n            order_by=[Register.id])\n        query = Register.select().order_by(fv)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"value\" FROM \"register\" AS \"t1\" '\n            'ORDER BY FIRST_VALUE(\"t1\".\"id\") '\n            'OVER (PARTITION BY \"t1\".\"value\" ORDER BY \"t1\".\"id\")'), [])\n\n    def test_window_extends(self):\n        Tbl = Table('tbl', ('b', 'c'))\n        w1 = Window(partition_by=[Tbl.b], alias='win1')\n        w2 = Window(extends=w1, order_by=[Tbl.c], alias='win2')\n        query = Tbl.select(fn.GROUP_CONCAT(Tbl.c).over(w2)).window(w1, w2)\n        self.assertSQL(query, (\n            'SELECT GROUP_CONCAT(\"t1\".\"c\") OVER \"win2\" FROM \"tbl\" AS \"t1\" '\n            'WINDOW \"win1\" AS (PARTITION BY \"t1\".\"b\"), '\n            '\"win2\" AS (\"win1\" ORDER BY \"t1\".\"c\")'), [])\n\n        w1 = Window(partition_by=[Tbl.b], alias='w1')\n        w2 = Window(extends=w1).alias('w2')\n        w3 = Window(extends=w2).alias('w3')\n        w4 = Window(extends=w3, order_by=[Tbl.c]).alias('w4')\n        query = (Tbl\n                 .select(fn.GROUP_CONCAT(Tbl.c).over(w4))\n                 .window(w1, w2, w3, w4))\n        self.assertSQL(query, (\n            'SELECT GROUP_CONCAT(\"t1\".\"c\") OVER \"w4\" FROM \"tbl\" AS \"t1\" '\n            'WINDOW \"w1\" AS (PARTITION BY \"t1\".\"b\"), \"w2\" AS (\"w1\"), '\n            '\"w3\" AS (\"w2\"), '\n            '\"w4\" AS (\"w3\" ORDER BY \"t1\".\"c\")'), [])\n\n    def test_window_ranged(self):\n        Tbl = Table('tbl', ('a', 'b'))\n        query = (Tbl\n                 .select(Tbl.a, fn.SUM(Tbl.b).over(\n                     order_by=[Tbl.a.desc()],\n                     frame_type=Window.RANGE,\n                     start=Window.preceding(1),\n                     end=Window.following(2)))\n                 .order_by(Tbl.a.asc()))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"a\", SUM(\"t1\".\"b\") OVER ('\n            'ORDER BY \"t1\".\"a\" DESC RANGE BETWEEN 1 PRECEDING AND 2 FOLLOWING)'\n            ' FROM \"tbl\" AS \"t1\" ORDER BY \"t1\".\"a\" ASC'), [])\n\n        query = (Tbl\n                 .select(Tbl.a, fn.SUM(Tbl.b).over(\n                     order_by=[Tbl.a],\n                     frame_type=Window.GROUPS,\n                     start=Window.preceding(3),\n                     end=Window.preceding(1))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"a\", SUM(\"t1\".\"b\") OVER ('\n            'ORDER BY \"t1\".\"a\" GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING) '\n            'FROM \"tbl\" AS \"t1\"'), [])\n\n        query = (Tbl\n                 .select(Tbl.a, fn.SUM(Tbl.b).over(\n                     order_by=[Tbl.a],\n                     frame_type=Window.GROUPS,\n                     start=Window.following(1),\n                     end=Window.following(5))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"a\", SUM(\"t1\".\"b\") OVER ('\n            'ORDER BY \"t1\".\"a\" GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING) '\n            'FROM \"tbl\" AS \"t1\"'), [])\n\n\n    def test_window_frametypes(self):\n        Tbl = Table('tbl', ('b', 'c'))\n        fts = (('as_range', Window.RANGE, 'RANGE'),\n               ('as_rows', Window.ROWS, 'ROWS'),\n               ('as_groups', Window.GROUPS, 'GROUPS'))\n        for method, arg, sql in fts:\n            w = getattr(Window(order_by=[Tbl.b + 1]), method)()\n            self.assertSQL(Tbl.select(fn.SUM(Tbl.c).over(w)).window(w), (\n                'SELECT SUM(\"t1\".\"c\") OVER \"w\" FROM \"tbl\" AS \"t1\" '\n                'WINDOW \"w\" AS (ORDER BY (\"t1\".\"b\" + ?) '\n                '%s UNBOUNDED PRECEDING)') % sql, [1])\n\n            query = Tbl.select(fn.SUM(Tbl.c)\n                               .over(order_by=[Tbl.b + 1], frame_type=arg))\n            self.assertSQL(query, (\n                'SELECT SUM(\"t1\".\"c\") OVER (ORDER BY (\"t1\".\"b\" + ?) '\n                '%s UNBOUNDED PRECEDING) FROM \"tbl\" AS \"t1\"') % sql, [1])\n\n    def test_window_frame_exclusion(self):\n        Tbl = Table('tbl', ('b', 'c'))\n        fts = ((Window.CURRENT_ROW, 'CURRENT ROW'),\n               (Window.TIES, 'TIES'),\n               (Window.NO_OTHERS, 'NO OTHERS'),\n               (Window.GROUP, 'GROUP'))\n        for arg, sql in fts:\n            query = Tbl.select(fn.MAX(Tbl.b).over(\n                order_by=[Tbl.c],\n                start=Window.preceding(4),\n                end=Window.following(),\n                frame_type=Window.ROWS,\n                exclude=arg))\n            self.assertSQL(query, (\n                'SELECT MAX(\"t1\".\"b\") OVER (ORDER BY \"t1\".\"c\" '\n                'ROWS BETWEEN 4 PRECEDING AND UNBOUNDED FOLLOWING '\n                'EXCLUDE %s) FROM \"tbl\" AS \"t1\"') % sql, [])\n\n    def test_filter_window(self):\n        # Example derived from sqlite window test 5.1.3.2.\n        Tbl = Table('tbl', ('a', 'c'))\n        win = Window(partition_by=fn.COALESCE(Tbl.a, ''),\n                     frame_type=Window.RANGE,\n                     start=Window.CURRENT_ROW,\n                     end=Window.following(),\n                     exclude=Window.NO_OTHERS)\n        query = (Tbl\n                 .select(fn.SUM(Tbl.c).filter(Tbl.c < 5).over(win),\n                         fn.RANK().over(win),\n                         fn.DENSE_RANK().over(win))\n                 .window(win))\n        self.assertSQL(query, (\n            'SELECT SUM(\"t1\".\"c\") FILTER (WHERE (\"t1\".\"c\" < ?)) OVER \"w\", '\n            'RANK() OVER \"w\", DENSE_RANK() OVER \"w\" '\n            'FROM \"tbl\" AS \"t1\" '\n            'WINDOW \"w\" AS (PARTITION BY COALESCE(\"t1\".\"a\", ?) '\n            'RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING '\n            'EXCLUDE NO OTHERS)'), [5, ''])\n\n\nclass TestValuesList(BaseTestCase):\n    _data = [(1, 'one'), (2, 'two'), (3, 'three')]\n\n    def test_values_list(self):\n        vl = ValuesList(self._data)\n\n        query = vl.select(SQL('*'))\n        self.assertSQL(query, (\n            'SELECT * FROM (VALUES (?, ?), (?, ?), (?, ?)) AS \"t1\"'),\n            [1, 'one', 2, 'two', 3, 'three'])\n\n    def test_values_list_named_columns(self):\n        vl = ValuesList(self._data).columns('idx', 'name')\n        query = (vl\n                 .select(vl.c.idx, vl.c.name)\n                 .order_by(vl.c.idx))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"idx\", \"t1\".\"name\" '\n            'FROM (VALUES (?, ?), (?, ?), (?, ?)) AS \"t1\"(\"idx\", \"name\") '\n            'ORDER BY \"t1\".\"idx\"'), [1, 'one', 2, 'two', 3, 'three'])\n\n    def test_named_values_list(self):\n        vl = ValuesList(self._data, ['idx', 'name']).alias('vl')\n        query = (vl\n                 .select(vl.c.idx, vl.c.name)\n                 .order_by(vl.c.idx))\n        self.assertSQL(query, (\n            'SELECT \"vl\".\"idx\", \"vl\".\"name\" '\n            'FROM (VALUES (?, ?), (?, ?), (?, ?)) AS \"vl\"(\"idx\", \"name\") '\n            'ORDER BY \"vl\".\"idx\"'), [1, 'one', 2, 'two', 3, 'three'])\n\n    def test_docs_examples(self):\n        data = [(1, 'first'), (2, 'second')]\n        vl = ValuesList(data, columns=('idx', 'name'))\n        query = (vl\n                 .select(vl.c.idx, vl.c.name)\n                 .order_by(vl.c.idx))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"idx\", \"t1\".\"name\" '\n            'FROM (VALUES (?, ?), (?, ?)) AS \"t1\"(\"idx\", \"name\") '\n            'ORDER BY \"t1\".\"idx\"'), [1, 'first', 2, 'second'])\n\n        vl = ValuesList([(1, 'first'), (2, 'second')])\n        vl = vl.columns('idx', 'name').alias('v')\n        query = vl.select(vl.c.idx, vl.c.name)\n        self.assertSQL(query, (\n            'SELECT \"v\".\"idx\", \"v\".\"name\" '\n            'FROM (VALUES (?, ?), (?, ?)) AS \"v\"(\"idx\", \"name\")'),\n            [1, 'first', 2, 'second'])\n\n    def test_join_on_valueslist(self):\n        vl = ValuesList([('huey',), ('zaizee',)], columns=['username'])\n        query = (User\n                 .select(vl.c.username)\n                 .join(vl, on=(User.c.username == vl.c.username))\n                 .order_by(vl.c.username.desc()))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"username\" FROM \"users\" AS \"t2\" '\n            'INNER JOIN (VALUES (?), (?)) AS \"t1\"(\"username\") '\n            'ON (\"t2\".\"username\" = \"t1\".\"username\") '\n            'ORDER BY \"t1\".\"username\" DESC'), ['huey', 'zaizee'])\n\n\nclass TestCaseFunction(BaseTestCase):\n    def test_case_function(self):\n        NameNum = Table('nn', ('name', 'number'))\n\n        query = (NameNum\n                 .select(NameNum.name, Case(NameNum.number, (\n                     (1, 'one'),\n                     (2, 'two')), '?').alias('num_str')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\", CASE \"t1\".\"number\" '\n            'WHEN ? THEN ? '\n            'WHEN ? THEN ? '\n            'ELSE ? END AS \"num_str\" '\n            'FROM \"nn\" AS \"t1\"'), [1, 'one', 2, 'two', '?'])\n\n        query = (NameNum\n                 .select(NameNum.name, Case(None, (\n                     (NameNum.number == 1, 'one'),\n                     (NameNum.number == 2, 'two')), '?')))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\", CASE '\n            'WHEN (\"t1\".\"number\" = ?) THEN ? '\n            'WHEN (\"t1\".\"number\" = ?) THEN ? '\n            'ELSE ? END '\n            'FROM \"nn\" AS \"t1\"'), [1, 'one', 2, 'two', '?'])\n\n    def test_multiple_case_expressions(self):\n        Sample = Table('sample', ('id', 'counter', 'value'))\n        case1 = Case(None, [\n            (Sample.counter < 5, 'low'),\n            (Sample.counter < 10, 'mid')],\n            'high').alias('tier')\n        case2 = Case(None, [\n            (Sample.value > 100, True)],\n            False).alias('is_large')\n        query = Sample.select(Sample.counter, case1, case2)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"counter\", '\n            'CASE WHEN (\"t1\".\"counter\" < ?) THEN ? '\n            'WHEN (\"t1\".\"counter\" < ?) THEN ? '\n            'ELSE ? END AS \"tier\", '\n            'CASE WHEN (\"t1\".\"value\" > ?) THEN ? '\n            'ELSE ? END AS \"is_large\" '\n            'FROM \"sample\" AS \"t1\"'),\n            [5, 'low', 10, 'mid', 'high', 100, True, False])\n\n    def test_case_subquery(self):\n        Name = Table('n', ('id', 'name',))\n        case = Case(None, [(Name.id.in_(Name.select(Name.id)), 1)], 0)\n        q = Name.select(fn.SUM(case))\n        self.assertSQL(q, (\n            'SELECT SUM('\n            'CASE WHEN (\"t1\".\"id\" IN (SELECT \"t1\".\"id\" FROM \"n\" AS \"t1\")) '\n            'THEN ? ELSE ? END) FROM \"n\" AS \"t1\"'), [1, 0])\n\n        case = Case(None, [\n            (Name.id < 5, Name.select(fn.SUM(Name.id))),\n            (Name.id > 5, Name.select(fn.COUNT(Name.name)).distinct())],\n            Name.select(fn.MAX(Name.id)))\n        q = Name.select(Name.name, case.alias('magic'))\n        self.assertSQL(q, (\n            'SELECT \"t1\".\"name\", CASE '\n            'WHEN (\"t1\".\"id\" < ?) '\n            'THEN (SELECT SUM(\"t1\".\"id\") FROM \"n\" AS \"t1\") '\n            'WHEN (\"t1\".\"id\" > ?) '\n            'THEN (SELECT DISTINCT COUNT(\"t1\".\"name\") FROM \"n\" AS \"t1\") '\n            'ELSE (SELECT MAX(\"t1\".\"id\") FROM \"n\" AS \"t1\") END AS \"magic\" '\n            'FROM \"n\" AS \"t1\"'), [5, 5])\n\n\n\nclass TestSelectFeatures(BaseTestCase):\n    def test_reselect(self):\n        query = Person.select(Person.name)\n        self.assertSQL(query, 'SELECT \"t1\".\"name\" FROM \"person\" AS \"t1\"', [])\n\n        query = query.columns(Person.id, Person.name, Person.dob)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\", \"t1\".\"dob\" '\n            'FROM \"person\" AS \"t1\"'), [])\n\n    def test_distinct_on(self):\n        query = (Note\n                 .select(Person.name, Note.content)\n                 .join(Person, on=(Note.person_id == Person.id))\n                 .order_by(Person.name, Note.content)\n                 .distinct(Person.name))\n        self.assertSQL(query, (\n            'SELECT DISTINCT ON (\"t1\".\"name\") '\n            '\"t1\".\"name\", \"t2\".\"content\" '\n            'FROM \"note\" AS \"t2\" '\n            'INNER JOIN \"person\" AS \"t1\" ON (\"t2\".\"person_id\" = \"t1\".\"id\") '\n            'ORDER BY \"t1\".\"name\", \"t2\".\"content\"'), [])\n\n        query = (Person\n                 .select(Person.name)\n                 .distinct(Person.name))\n        self.assertSQL(query, (\n            'SELECT DISTINCT ON (\"t1\".\"name\") \"t1\".\"name\" '\n            'FROM \"person\" AS \"t1\"'), [])\n\n    def test_distinct(self):\n        query = Person.select(Person.name).distinct()\n        self.assertSQL(query,\n                       'SELECT DISTINCT \"t1\".\"name\" FROM \"person\" AS \"t1\"', [])\n\n    def test_distinct_count(self):\n        query = Person.select(fn.COUNT(Person.name.distinct()))\n        self.assertSQL(query, (\n            'SELECT COUNT(DISTINCT \"t1\".\"name\") FROM \"person\" AS \"t1\"'), [])\n\n    def test_filtered_count(self):\n        filtered_count = (fn.COUNT(Person.name)\n                          .filter(Person.dob < datetime.date(2000, 1, 1)))\n        query = Person.select(fn.COUNT(Person.name), filtered_count)\n        self.assertSQL(query, (\n            'SELECT COUNT(\"t1\".\"name\"), COUNT(\"t1\".\"name\") '\n            'FILTER (WHERE (\"t1\".\"dob\" < ?)) '\n            'FROM \"person\" AS \"t1\"'), [datetime.date(2000, 1, 1)])\n\n    def test_ordered_aggregate(self):\n        agg = fn.array_agg(Person.name).order_by(Person.id.desc())\n        self.assertSQL(Person.select(agg.alias('names')), (\n            'SELECT array_agg(\"t1\".\"name\" ORDER BY \"t1\".\"id\" DESC) AS \"names\" '\n            'FROM \"person\" AS \"t1\"'), [])\n\n        agg = fn.string_agg(Person.name, ',').order_by(Person.dob, Person.id)\n        self.assertSQL(Person.select(agg), (\n            'SELECT string_agg(\"t1\".\"name\", ? ORDER BY \"t1\".\"dob\", \"t1\".\"id\")'\n            ' FROM \"person\" AS \"t1\"'), [','])\n\n        agg = (fn.string_agg(Person.name.concat('-x'), ',')\n               .order_by(Person.name.desc(), Person.dob.asc()))\n        self.assertSQL(Person.select(agg), (\n            'SELECT string_agg((\"t1\".\"name\" || ?), ? ORDER BY \"t1\".\"name\" DESC'\n            ', \"t1\".\"dob\" ASC) '\n            'FROM \"person\" AS \"t1\"'), ['-x', ','])\n\n        agg = agg.order_by()\n        self.assertSQL(Person.select(agg), (\n            'SELECT string_agg((\"t1\".\"name\" || ?), ?) '\n            'FROM \"person\" AS \"t1\"'), ['-x', ','])\n\n    def test_for_update(self):\n        query = (Person\n                 .select()\n                 .where(Person.name == 'charlie')\n                 .for_update())\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\", \"t1\".\"dob\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = ?) '\n            'FOR UPDATE'), ['charlie'], for_update=True)\n\n        query = query.for_update('FOR SHARE NOWAIT')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"name\", \"t1\".\"dob\" '\n            'FROM \"person\" AS \"t1\" '\n            'WHERE (\"t1\".\"name\" = ?) '\n            'FOR SHARE NOWAIT'), ['charlie'], for_update=True)\n\n    def test_for_update_nested(self):\n        PA = Person.alias('pa')\n        subq = PA.select(PA.id).where(PA.name == 'charlie').for_update()\n        query = (Person\n                 .delete()\n                 .where(Person.id.in_(subq)))\n        self.assertSQL(query, (\n            'DELETE FROM \"person\" WHERE (\"person\".\"id\" IN ('\n            'SELECT \"pa\".\"id\" FROM \"person\" AS \"pa\" '\n            'WHERE (\"pa\".\"name\" = ?) FOR UPDATE))'),\n            ['charlie'],\n            for_update=True)\n\n    def test_for_update_options(self):\n        query = (Person\n                 .select(Person.id)\n                 .where(Person.name == 'huey')\n                 .for_update(of=Person, nowait=True))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" WHERE (\"t1\".\"name\" = ?) '\n            'FOR UPDATE OF \"t1\" NOWAIT'), ['huey'], for_update=True)\n\n        # Check default behavior.\n        query = query.for_update()\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" WHERE (\"t1\".\"name\" = ?) '\n            'FOR UPDATE'), ['huey'], for_update=True)\n\n        # Clear flag.\n        query = query.for_update(None)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" WHERE (\"t1\".\"name\" = ?)'),\n            ['huey'])\n\n        # Old-style is still supported.\n        query = query.for_update('FOR UPDATE NOWAIT')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" WHERE (\"t1\".\"name\" = ?) '\n            'FOR UPDATE NOWAIT'), ['huey'], for_update=True)\n\n        # Mix of old and new is OK.\n        query = query.for_update('FOR SHARE NOWAIT', of=Person)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\" FROM \"person\" AS \"t1\" WHERE (\"t1\".\"name\" = ?) '\n            'FOR SHARE OF \"t1\" NOWAIT'), ['huey'], for_update=True)\n\n    def test_parentheses(self):\n        query = (Person\n                 .select(fn.MAX(\n                     fn.IFNULL(1, 10) * 151,\n                     fn.IFNULL(None, 10))))\n        self.assertSQL(query, (\n            'SELECT MAX((IFNULL(?, ?) * ?), IFNULL(?, ?)) '\n            'FROM \"person\" AS \"t1\"'), [1, 10, 151, None, 10])\n\n        query = (Person\n                 .select(Person.name)\n                 .where(fn.EXISTS(\n                     User.select(User.c.id).where(\n                         User.c.username == Person.name))))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"name\" FROM \"person\" AS \"t1\" '\n            'WHERE EXISTS('\n            'SELECT \"t2\".\"id\" FROM \"users\" AS \"t2\" '\n            'WHERE (\"t2\".\"username\" = \"t1\".\"name\"))'), [])\n\n\nclass TestExpressionSQL(BaseTestCase):\n    def test_parentheses_functions(self):\n        expr = (User.c.income + 100)\n        expr2 = expr * expr\n        query = User.select(fn.sum(expr), fn.avg(expr2))\n        self.assertSQL(query, (\n            'SELECT sum(\"t1\".\"income\" + ?), '\n            'avg((\"t1\".\"income\" + ?) * (\"t1\".\"income\" + ?)) '\n            'FROM \"users\" AS \"t1\"'), [100, 100, 100])\n\n\n#Person = Table('person', ['id', 'name', 'dob'])\n\nclass TestOnConflictSqlite(BaseTestCase):\n    database = SqliteDatabase(None)\n\n    def test_replace(self):\n        query = Person.insert(name='huey').on_conflict('replace')\n        self.assertSQL(query, (\n            'INSERT OR REPLACE INTO \"person\" (\"name\") VALUES (?)'), ['huey'])\n\n    def test_ignore(self):\n        query = Person.insert(name='huey').on_conflict('ignore')\n        self.assertSQL(query, (\n            'INSERT OR IGNORE INTO \"person\" (\"name\") VALUES (?)'), ['huey'])\n\n    def test_update_not_supported(self):\n        query = Person.insert(name='huey').on_conflict(\n            preserve=(Person.dob,),\n            update={Person.name: Person.name.concat(' (updated)')})\n        with self.assertRaisesCtx(ValueError):\n            self.database.get_sql_context().parse(query)\n\n\nclass TestOnConflictMySQL(BaseTestCase):\n    database = MySQLDatabase(None)\n\n    def setUp(self):\n        super(TestOnConflictMySQL, self).setUp()\n        self.database.server_version = None\n\n    def test_replace(self):\n        query = Person.insert(name='huey').on_conflict('replace')\n        self.assertSQL(query, (\n            'REPLACE INTO \"person\" (\"name\") VALUES (?)'), ['huey'])\n\n    def test_ignore(self):\n        query = Person.insert(name='huey').on_conflict('ignore')\n        self.assertSQL(query, (\n            'INSERT IGNORE INTO \"person\" (\"name\") VALUES (?)'), ['huey'])\n\n    def test_update(self):\n        dob = datetime.date(2010, 1, 1)\n        query = (Person\n                 .insert(name='huey', dob=dob)\n                 .on_conflict(\n                     preserve=(Person.dob,),\n                     update={Person.name: Person.name.concat('-x')}))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") VALUES (?, ?) '\n            'ON DUPLICATE KEY '\n            'UPDATE \"dob\" = VALUES(\"dob\"), \"name\" = (\"name\" || ?)'),\n            [dob, 'huey', '-x'])\n\n        query = (Person\n                 .insert(name='huey', dob=dob)\n                 .on_conflict(preserve='dob'))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") VALUES (?, ?) '\n            'ON DUPLICATE KEY '\n            'UPDATE \"dob\" = VALUES(\"dob\")'), [dob, 'huey'])\n\n    def test_update_use_value_mariadb(self):\n        # Verify that we use \"VALUE\" (not \"VALUES\") for MariaDB 10.3.3.\n        dob = datetime.date(2010, 1, 1)\n        query = (Person\n                 .insert(name='huey', dob=dob)\n                 .on_conflict(preserve=(Person.dob,)))\n        self.database.server_version = (10, 3, 3)\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") VALUES (?, ?) '\n            'ON DUPLICATE KEY '\n            'UPDATE \"dob\" = VALUE(\"dob\")'), [dob, 'huey'])\n\n        self.database.server_version = (10, 3, 2)\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") VALUES (?, ?) '\n            'ON DUPLICATE KEY '\n            'UPDATE \"dob\" = VALUES(\"dob\")'), [dob, 'huey'])\n\n    def test_where_not_supported(self):\n        query = Person.insert(name='huey').on_conflict(\n            preserve=(Person.dob,),\n            where=(Person.name == 'huey'))\n        with self.assertRaisesCtx(ValueError):\n            self.database.get_sql_context().parse(query)\n\n\nclass TestOnConflictPostgresql(BaseTestCase):\n    database = PostgresqlDatabase(None)\n\n    def test_ignore(self):\n        query = Person.insert(name='huey').on_conflict('ignore')\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?) '\n            'ON CONFLICT DO NOTHING'), ['huey'])\n\n    def test_conflict_target_required(self):\n        query = Person.insert(name='huey').on_conflict(preserve=(Person.dob,))\n        with self.assertRaisesCtx(ValueError):\n            self.database.get_sql_context().parse(query)\n\n    def test_conflict_resolution_required(self):\n        query = Person.insert(name='huey').on_conflict(conflict_target='name')\n        with self.assertRaisesCtx(ValueError):\n            self.database.get_sql_context().parse(query)\n\n    def test_conflict_update_excluded(self):\n        KV = Table('kv', ('key', 'value', 'extra'), _database=self.database)\n\n        query = (KV.insert(key='k1', value='v1', extra=1)\n                 .on_conflict(conflict_target=(KV.key, KV.value),\n                              update={KV.extra: EXCLUDED.extra + 2},\n                              where=(EXCLUDED.extra < KV.extra)))\n        self.assertSQL(query, (\n            'INSERT INTO \"kv\" (\"extra\", \"key\", \"value\") VALUES (?, ?, ?) '\n            'ON CONFLICT (\"key\", \"value\") DO UPDATE '\n            'SET \"extra\" = (EXCLUDED.\"extra\" + ?) '\n            'WHERE (EXCLUDED.\"extra\" < \"kv\".\"extra\")'), [1, 'k1', 'v1', 2])\n\n    def test_conflict_target_or_constraint(self):\n        KV = Table('kv', ('key', 'value', 'extra'), _database=self.database)\n\n        query = (KV.insert(key='k1', value='v1', extra='e1')\n                 .on_conflict(conflict_target=[KV.key, KV.value],\n                              preserve=[KV.extra]))\n        self.assertSQL(query, (\n            'INSERT INTO \"kv\" (\"extra\", \"key\", \"value\") VALUES (?, ?, ?) '\n            'ON CONFLICT (\"key\", \"value\") DO UPDATE '\n            'SET \"extra\" = EXCLUDED.\"extra\"'), ['e1', 'k1', 'v1'])\n\n        query = (KV.insert(key='k1', value='v1', extra='e1')\n                 .on_conflict(conflict_constraint='kv_key_value',\n                              preserve=[KV.extra]))\n        self.assertSQL(query, (\n            'INSERT INTO \"kv\" (\"extra\", \"key\", \"value\") VALUES (?, ?, ?) '\n            'ON CONFLICT ON CONSTRAINT \"kv_key_value\" DO UPDATE '\n            'SET \"extra\" = EXCLUDED.\"extra\"'), ['e1', 'k1', 'v1'])\n\n        query = KV.insert(key='k1', value='v1', extra='e1')\n        self.assertRaises(ValueError, query.on_conflict,\n                          conflict_target=[KV.key, KV.value],\n                          conflict_constraint='kv_key_value')\n\n    def test_update(self):\n        dob = datetime.date(2010, 1, 1)\n        query = (Person\n                 .insert(name='huey', dob=dob)\n                 .on_conflict(\n                     conflict_target=(Person.name,),\n                     preserve=(Person.dob,),\n                     update={Person.name: Person.name.concat('-x')}))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") VALUES (?, ?) '\n            'ON CONFLICT (\"name\") DO '\n            'UPDATE SET \"dob\" = EXCLUDED.\"dob\", '\n            '\"name\" = (\"person\".\"name\" || ?)'),\n            [dob, 'huey', '-x'])\n\n        query = (Person\n                 .insert(name='huey', dob=dob)\n                 .on_conflict(\n                     conflict_target='name',\n                     preserve='dob'))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"dob\", \"name\") VALUES (?, ?) '\n            'ON CONFLICT (\"name\") DO '\n            'UPDATE SET \"dob\" = EXCLUDED.\"dob\"'), [dob, 'huey'])\n\n        query = (Person\n                 .insert(name='huey')\n                 .on_conflict(\n                     conflict_target=Person.name,\n                     preserve=Person.dob,\n                     update={Person.name: Person.name.concat('-x')},\n                     where=(Person.name != 'zaizee')))\n        self.assertSQL(query, (\n            'INSERT INTO \"person\" (\"name\") VALUES (?) '\n            'ON CONFLICT (\"name\") DO '\n            'UPDATE SET \"dob\" = EXCLUDED.\"dob\", '\n            '\"name\" = (\"person\".\"name\" || ?) '\n            'WHERE (\"person\".\"name\" != ?)'), ['huey', '-x', 'zaizee'])\n\n    def test_conflict_target_partial_index(self):\n        KVE = Table('kve', ('key', 'value', 'extra'))\n        data = [('k1', 1, 2), ('k2', 2, 3)]\n        columns = [KVE.key, KVE.value, KVE.extra]\n\n        query = (KVE\n                 .insert(data, columns)\n                 .on_conflict(\n                     conflict_target=(KVE.key, KVE.value),\n                     conflict_where=(KVE.extra > 1),\n                     preserve=(KVE.extra,),\n                     where=(KVE.key != 'kx')))\n        self.assertSQL(query, (\n            'INSERT INTO \"kve\" (\"key\", \"value\", \"extra\") '\n            'VALUES (?, ?, ?), (?, ?, ?) '\n            'ON CONFLICT (\"key\", \"value\") WHERE (\"extra\" > ?) '\n            'DO UPDATE SET \"extra\" = EXCLUDED.\"extra\" '\n            'WHERE (\"kve\".\"key\" != ?)'),\n            ['k1', 1, 2, 'k2', 2, 3, 1, 'kx'])\n\n\n#Person = Table('person', ['id', 'name', 'dob'])\n#Note = Table('note', ['id', 'person_id', 'content'])\n\nclass TestIndex(BaseTestCase):\n    def test_simple_index(self):\n        pidx = Index('person_name', Person, (Person.name,), unique=True)\n        self.assertSQL(pidx, (\n            'CREATE UNIQUE INDEX \"person_name\" ON \"person\" (\"name\")'), [])\n\n        pidx = pidx.where(Person.dob > datetime.date(1950, 1, 1))\n        self.assertSQL(pidx, (\n            'CREATE UNIQUE INDEX \"person_name\" ON \"person\" '\n            '(\"name\") WHERE (\"dob\" > ?)'), [datetime.date(1950, 1, 1)])\n\n    def test_advanced_index(self):\n        Article = Table('article')\n        aidx = Index('foo_idx', Article, (\n            Article.c.status,\n            Article.c.timestamp.desc(),\n            fn.SUBSTR(Article.c.title, 1, 1)), safe=True)\n        self.assertSQL(aidx, (\n            'CREATE INDEX IF NOT EXISTS \"foo_idx\" ON \"article\" '\n            '(\"status\", \"timestamp\" DESC, SUBSTR(\"title\", ?, ?))'), [1, 1])\n\n        aidx = aidx.where(Article.c.flags.bin_and(4) == 4)\n        self.assertSQL(aidx, (\n            'CREATE INDEX IF NOT EXISTS \"foo_idx\" ON \"article\" '\n            '(\"status\", \"timestamp\" DESC, SUBSTR(\"title\", ?, ?)) '\n            'WHERE ((\"flags\" & ?) = ?)'), [1, 1, 4, 4])\n\n        # Check behavior when value-literals are enabled.\n        self.assertSQL(aidx, (\n            'CREATE INDEX IF NOT EXISTS \"foo_idx\" ON \"article\" '\n            '(\"status\", \"timestamp\" DESC, SUBSTR(\"title\", 1, 1)) '\n            'WHERE ((\"flags\" & 4) = 4)'), [], value_literals=True)\n\n    def test_str_cols(self):\n        uidx = Index('users_info', User, ('username DESC', 'id'))\n        self.assertSQL(uidx, (\n            'CREATE INDEX \"users_info\" ON \"users\" (username DESC, id)'), [])\n\n\nclass TestSqlToString(BaseTestCase):\n    def _test_sql_to_string(self, _param):\n        class FakeDB(SqliteDatabase):\n            param = _param\n\n        db = FakeDB(None)\n        T = Table('tbl', ('id', 'val')).bind(db)\n\n        query = (T.select()\n                 .where((T.val == 'foo') |\n                        (T.val == b'bar') |\n                        (T.val == True) | (T.val == False) |\n                        (T.val == 2) |\n                        (T.val == -3.14) |\n                        (T.val == datetime.datetime(2018, 1, 1)) |\n                        (T.val == datetime.date(2018, 1, 2)) |\n                        T.val.is_null() |\n                        T.val.is_null(False) |\n                        T.val.in_(['aa', 'bb', 'cc'])))\n\n        self.assertEqual(query_to_string(query), (\n            'SELECT \"t1\".\"id\", \"t1\".\"val\" FROM \"tbl\" AS \"t1\" WHERE ((((((((((('\n            '\"t1\".\"val\" = \\'foo\\') OR '\n            '(\"t1\".\"val\" = \\'bar\\')) OR '\n            '(\"t1\".\"val\" = 1)) OR '\n            '(\"t1\".\"val\" = 0)) OR '\n            '(\"t1\".\"val\" = 2)) OR '\n            '(\"t1\".\"val\" = -3.14)) OR '\n            '(\"t1\".\"val\" = \\'2018-01-01 00:00:00\\')) OR '\n            '(\"t1\".\"val\" = \\'2018-01-02\\')) OR '\n            '(\"t1\".\"val\" IS NULL)) OR '\n            '(\"t1\".\"val\" IS NOT NULL)) OR '\n            '(\"t1\".\"val\" IN (\\'aa\\', \\'bb\\', \\'cc\\')))'))\n\n    def test_sql_to_string_qmark(self):\n        self._test_sql_to_string('?')\n\n    def test_sql_to_string_default(self):\n        self._test_sql_to_string('%s')\n"
  },
  {
    "path": "tests/sqlcipher_ext.py",
    "content": "import datetime\nimport os\nfrom hashlib import sha1\n\nfrom peewee import DatabaseError\nfrom playhouse.sqlcipher_ext import *\nfrom playhouse.sqlite_ext import *\n\nfrom .base import ModelTestCase\nfrom .base import TestModel\n\n\nPASSPHRASE = 'testing sqlcipher'\nPRAGMAS = {\n    'kdf_iter': 10,   # Much faster for testing. Totally unsafe.\n    'cipher_log_level': 'none',\n}\ndb = SqlCipherDatabase('peewee_test.dbc', passphrase=PASSPHRASE,\n                       pragmas=PRAGMAS, rank_functions=True)\n\n\n@db.func('shazam')\ndef shazam(s):\n    return sha1((s or '').encode('utf-8')).hexdigest()[:5]\n\n\nclass Thing(TestModel):\n    name = CharField()\n\n\nclass FTSNote(FTSModel, TestModel):\n    content = TextField()\n\n\nclass Note(TestModel):\n    content = TextField()\n    timestamp = DateTimeField(default=datetime.datetime.now)\n\n\nclass CleanUpModelTestCase(ModelTestCase):\n    def tearDown(self):\n        super(CleanUpModelTestCase, self).tearDown()\n        if os.path.exists(self.database.database):\n            os.unlink(self.database.database)\n\n\nclass SqlCipherTestCase(CleanUpModelTestCase):\n    database = db\n    requires = [Thing]\n\n    def test_good_and_bad_passphrases(self):\n        things = ('t1', 't2', 't3')\n        for thing in things:\n            Thing.create(name=thing)\n\n        # Try to open db with wrong passphrase\n        bad_db = SqlCipherDatabase(db.database, passphrase='wrong passphrase')\n        self.assertRaises(DatabaseError, bad_db.get_tables)\n\n        # Assert that we can still access the data with the good passphrase.\n        query = Thing.select().order_by(Thing.name)\n        self.assertEqual([t.name for t in query], ['t1', 't2', 't3'])\n\n    def test_rekey(self):\n        things = ('t1', 't2', 't3')\n        for thing in things:\n            Thing.create(name=thing)\n\n        self.database.rekey('a new passphrase')\n\n        db2 = SqlCipherDatabase(db.database, passphrase='a new passphrase',\n                                pragmas=PRAGMAS)\n        cursor = db2.execute_sql('select name from thing order by name;')\n        self.assertEqual([name for name, in cursor], ['t1', 't2', 't3'])\n\n        query = Thing.select().order_by(Thing.name)\n        self.assertEqual([t.name for t in query], ['t1', 't2', 't3'])\n\n        self.database.close()\n        self.database.connect()\n\n        query = Thing.select().order_by(Thing.name)\n        self.assertEqual([t.name for t in query], ['t1', 't2', 't3'])\n\n        # Re-set to the original passphrase.\n        self.database.rekey(PASSPHRASE)\n\n    def test_empty_passphrase(self):\n        db = SqlCipherDatabase(':memory:')\n\n        class CM(TestModel):\n            data = TextField()\n            class Meta:\n                database = db\n\n        db.connect()\n        db.create_tables([CM])\n        cm = CM.create(data='foo')\n        cm_db = CM.get(CM.data == 'foo')\n        self.assertEqual(cm_db.id, cm.id)\n        self.assertEqual(cm_db.data, 'foo')\n\n\nconfig_db = SqlCipherDatabase('peewee_test.dbc', pragmas={\n    'kdf_iter': 1234,\n    'cipher_page_size': 8192}, passphrase=PASSPHRASE)\n\nclass TestSqlCipherConfiguration(CleanUpModelTestCase):\n    database = config_db\n\n    def test_configuration_via_pragma(self):\n        # Write some data so the database file is created.\n        self.database.execute_sql('create table foo (data TEXT)')\n        self.database.close()\n\n        self.database.connect()\n        self.assertEqual(int(self.database.pragma('kdf_iter')), 1234)\n        self.assertEqual(int(self.database.pragma('cipher_page_size')), 8192)\n        self.assertTrue('foo' in self.database.get_tables())\n\n\nclass SqlCipherExtTestCase(CleanUpModelTestCase):\n    database = db\n    requires = [Note]\n\n    def setUp(self):\n        super(SqlCipherExtTestCase, self).setUp()\n        FTSNote._meta.database = db\n        FTSNote.drop_table(True)\n        FTSNote.create_table(tokenize='porter', content=Note.content)\n\n    def tearDown(self):\n        FTSNote.drop_table(True)\n        super(SqlCipherExtTestCase, self).tearDown()\n\n    def test_fts(self):\n        strings = [\n            'python and peewee for working with databases',\n            'relational databases are the best',\n            'sqlite is the best relational database',\n            'sqlcipher is a cool database extension']\n        for s in strings:\n            Note.create(content=s)\n        FTSNote.rebuild()\n\n        query = (FTSNote\n                 .select(FTSNote, FTSNote.rank().alias('score'))\n                 .where(FTSNote.match('relational databases'))\n                 .order_by(SQL('score').desc()))\n        notes = [note.content for note in query]\n        self.assertEqual(notes, [\n            'relational databases are the best',\n            'sqlite is the best relational database'])\n\n        alt_conn = SqliteDatabase(db.database)\n        self.assertRaises(\n            DatabaseError,\n            alt_conn.execute_sql,\n            'SELECT * FROM \"%s\"' % (FTSNote._meta.table_name))\n\n    def test_func(self):\n        Note.create(content='hello')\n        Note.create(content='baz')\n        Note.create(content='nug')\n\n        query = (Note\n                 .select(Note.content, fn.shazam(Note.content).alias('shz'))\n                 .order_by(Note.id)\n                 .dicts())\n        results = list(query)\n        self.assertEqual(results, [\n            {'content': 'hello', 'shz': 'aaf4c'},\n            {'content': 'baz', 'shz': 'bbe96'},\n            {'content': 'nug', 'shz': '52616'},\n        ])\n"
  },
  {
    "path": "tests/sqlite.py",
    "content": "from decimal import Decimal as D\nimport datetime\nimport os\nimport sys\n\nfrom peewee import *\nfrom peewee import sqlite3\nfrom playhouse.sqlite_ext import *\n\nfrom .base import BaseTestCase\nfrom .base import IS_SQLITE_37\nfrom .base import IS_SQLITE_9\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import get_in_memory_db\nfrom .base import get_sqlite_db\nfrom .base import requires_models\nfrom .base import skip_if\nfrom .base import skip_unless\nfrom .base_models import Person\nfrom .base_models import Tweet\nfrom .base_models import User\nfrom .sqlite_helpers import compile_option\nfrom .sqlite_helpers import json_installed\nfrom .sqlite_helpers import json_patch_installed\nfrom .sqlite_helpers import json_text_installed\nfrom .sqlite_helpers import jsonb_installed\n\n\ndatabase = SqliteDatabase(':memory:', rank_functions=True, timeout=100)\n\n\ntry:\n    from playhouse._sqlite_udf import peewee_rank\n    CYTHON_EXTENSION = True\nexcept ImportError:\n    CYTHON_EXTENSION = False\n\n\nclass WeightedAverage(object):\n    def __init__(self):\n        self.total = 0.\n        self.count = 0.\n\n    def step(self, value, weight=None):\n        weight = weight or 1.\n        self.total += weight\n        self.count += (weight * value)\n\n    def finalize(self):\n        if self.total != 0.:\n            return self.count / self.total\n        return 0.\n\ndef _cmp(l, r):\n    if l < r:\n        return -1\n    return 1 if r < l else 0\n\ndef collate_reverse(s1, s2):\n    return -_cmp(s1, s2)\n\n@database.collation()\ndef collate_case_insensitive(s1, s2):\n    return _cmp(s1.lower(), s2.lower())\n\ndef title_case(s): return s.title()\n\n@database.func()\ndef rstrip(s, n):\n    return s.rstrip(n)\n\ndatabase.register_aggregate(WeightedAverage, 'weighted_avg', 1)\ndatabase.register_aggregate(WeightedAverage, 'weighted_avg2', 2)\ndatabase.register_collation(collate_reverse)\ndatabase.register_function(title_case)\n\n\nclass Post(TestModel):\n    message = TextField()\n\n\nclass ContentPost(FTSModel, Post):\n    class Meta:\n        options = {\n            'content': Post,\n            'tokenize': 'porter'}\n\n\nclass ContentPostMessage(FTSModel, TestModel):\n    message = TextField()\n    class Meta:\n        options = {'tokenize': 'porter', 'content': Post.message}\n\n\nclass Document(FTSModel, TestModel):\n    message = TextField()\n    class Meta:\n        options = {'tokenize': 'porter'}\n\n\nclass MultiColumn(FTSModel, TestModel):\n    c1 = SearchField()\n    c2 = SearchField()\n    c3 = SearchField()\n    c4 = IntegerField()\n    class Meta:\n        options = {'tokenize': 'porter'}\n\n\nclass RowIDModel(TestModel):\n    rowid = RowIDField()\n    data = IntegerField()\n\n\nclass KeyData(TestModel):\n    key = TextField()\n    data = JSONField()\n\nclass JBData(TestModel):\n    key = TextField()\n    data = JSONBField()\n\n\nclass Values(TestModel):\n    klass = IntegerField()\n    value = FloatField()\n    weight = FloatField()\n\n\nclass FTS5Test(FTS5Model):\n    title = SearchField()\n    data = SearchField()\n    misc = SearchField(unindexed=True)\n\n    class Meta:\n        legacy_table_names = False\n\n\nclass FTS5Document(FTS5Model):\n    message = SearchField()\n    class Meta:\n        options = {'tokenize': 'porter'}\n\n\nclass DT(TestModel):\n    key = TextField(primary_key=True)\n    d = DateTimeField()\n    iso = ISODateTimeField()\n\n\n@skip_unless(json_installed(), 'requires sqlite json1')\nclass TestJSONField(ModelTestCase):\n    database = database\n    requires = [KeyData]\n\n    def test_schema(self):\n        self.assertSQL(KeyData._schema._create_table(), (\n            'CREATE TABLE IF NOT EXISTS \"key_data\" ('\n            '\"id\" INTEGER NOT NULL PRIMARY KEY, '\n            '\"key\" TEXT NOT NULL, '\n            '\"data\" JSON NOT NULL)'), [])\n\n    def test_create_read_update(self):\n        test_values = (\n            'simple string',\n            '',\n            1337,\n            0.0,\n            True,\n            False,\n            ['foo', 'bar', ['baz', 'nug']],\n            {'k1': 'v1', 'k2': {'x1': 'y1', 'x2': 'y2'}},\n            {'a': 1, 'b': 0.0, 'c': True, 'd': False, 'e': None, 'f': [0, 1],\n             'g': {'h': 'ijkl'}},\n        )\n\n        # Create a row using the given test value. Verify we can read the value\n        # back from the database, and also that we can query for the row using\n        # the value in the WHERE clause.\n        for i, value in enumerate(test_values):\n            # We can create and re-read values.\n            KeyData.create(key='k%s' % i, data=value)\n            kd_db = KeyData.get(KeyData.key == 'k%s' % i)\n            self.assertEqual(kd_db.data, value)\n\n            # We can read the data back using the value in the WHERE clause.\n            kd_db = KeyData.get(KeyData.data == value)\n            self.assertEqual(kd_db.key, 'k%s' % i)\n\n        # Verify we can use values in UPDATE query.\n        kd = KeyData.create(key='kx', data='')\n        for value in test_values:\n            nrows = (KeyData\n                     .update(data=value)\n                     .where(KeyData.key == 'kx')\n                     .execute())\n            self.assertEqual(nrows, 1)\n            kd_db = KeyData.get(KeyData.key == 'kx')\n            self.assertEqual(kd_db.data, value)\n\n    def test_json_unicode(self):\n        with self.database.atomic():\n            KeyData.delete().execute()\n\n        # Two Chinese characters.\n        unicode_str = b'\\xe4\\xb8\\xad\\xe6\\x96\\x87'.decode('utf8')\n        data = {'foo': unicode_str}\n        kd = KeyData.create(key='k1', data=data)\n\n        kd_db = KeyData.get(KeyData.key == 'k1')\n        self.assertEqual(kd_db.data, {'foo': unicode_str})\n\n    def test_json_to_json(self):\n        kd1 = KeyData.create(key='k1', data={'k1': 'v1', 'k2': 'v2'})\n        subq = (KeyData\n                .select(KeyData.data)\n                .where(KeyData.key == 'k1'))\n\n        # Assign value using a subquery.\n        KeyData.create(key='k2', data=subq)\n        kd2_db = KeyData.get(KeyData.key == 'k2')\n        self.assertEqual(kd2_db.data, {'k1': 'v1', 'k2': 'v2'})\n\n    def test_json_bulk_update_top_level_list(self):\n        kd1 = KeyData.create(key='k1', data=['a', 'b', 'c'])\n        kd2 = KeyData.create(key='k2', data=['d', 'e', 'f'])\n\n        kd1.data = ['g', 'h', 'i']\n        kd2.data = ['j', 'k', 'l']\n        KeyData.bulk_update([kd1, kd2], fields=[KeyData.data])\n        kd1_db = KeyData.get(KeyData.key == 'k1')\n        kd2_db = KeyData.get(KeyData.key == 'k2')\n        self.assertEqual(kd1_db.data, ['g', 'h', 'i'])\n        self.assertEqual(kd2_db.data, ['j', 'k', 'l'])\n\n    def test_json_bulk_update_top_level_dict(self):\n        kd1 = KeyData.create(key='k1', data={'x': 'y1'})\n        kd2 = KeyData.create(key='k2', data={'x': 'y2'})\n\n        kd1.data = {'x': 'z1'}\n        kd2.data = {'X': 'Z2'}\n        KeyData.bulk_update([kd1, kd2], fields=[KeyData.data])\n        kd1_db = KeyData.get(KeyData.key == 'k1')\n        kd2_db = KeyData.get(KeyData.key == 'k2')\n        self.assertEqual(kd1_db.data, {'x': 'z1'})\n        self.assertEqual(kd2_db.data, {'X': 'Z2'})\n\n    def test_json_multi_ops(self):\n        data = (\n            ('k1', [0, 1]),\n            ('k2', [1, 2]),\n            ('k3', {'x3': 'y3'}),\n            ('k4', {'x4': 'y4'}))\n        res = KeyData.insert_many(data).execute()\n        if database.returning_clause:\n            self.assertEqual([r for r, in res], [1, 2, 3, 4])\n        else:\n            self.assertEqual(res, 4)\n\n        vals = [[1, 2], [2, 3], {'x3': 'y3'}, {'x5': 'y5'}]\n        pw_vals = [Value(v, unpack=False) for v in vals]\n\n        query = KeyData.select().where(KeyData.data.in_(pw_vals))\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"id\", \"t1\".\"key\", \"t1\".\"data\" '\n            'FROM \"key_data\" AS \"t1\" '\n            'WHERE (\"t1\".\"data\" IN (json(?), json(?), json(?), json(?)))'),\n            ['[1, 2]', '[2, 3]', '{\"x3\": \"y3\"}', '{\"x5\": \"y5\"}'])\n\n        self.assertEqual(query.count(), 2)\n        self.assertEqual(sorted([k.key for k in query]), ['k2', 'k3'])\n\n        query = KeyData.select().where(KeyData.data == [1, 2])\n        self.assertEqual(query.count(), 1)\n        self.assertEqual(query.get().key, 'k2')\n\n        query = KeyData.select().where(KeyData.data == {'x3': 'y3'})\n        self.assertEqual(query.count(), 1)\n        self.assertEqual(query.get().key, 'k3')\n\n    def test_select_json_value(self):\n        data = (\n            ('k1', {'a': {'b': 'c', 'd': [2, 1, 0]}}),\n        )\n        KeyData.insert_many(data).execute()\n\n        kd = (KeyData\n              .select(KeyData.data['a'].alias('a'))\n              .get())\n        self.assertEqual(kd.a, {'b': 'c', 'd': [2, 1, 0]})\n\n        kd = (KeyData\n              .select(KeyData.data['a']['b'].alias('b'))\n              .get())\n        self.assertEqual(kd.b, 'c')\n\n        kd = (KeyData\n              .select(KeyData.data['a']['d'].alias('d'))\n              .get())\n        self.assertEqual(kd.d, [2, 1, 0])\n\n        kd = (KeyData\n              .select(KeyData.data['a']['d'][0].alias('d0'))\n              .get())\n        self.assertEqual(kd.d0, 2)\n\n\n@skip_unless(json_installed(), 'requires sqlite json1')\nclass TestJSONFieldFunctions(ModelTestCase):\n    database = database\n    requires = [KeyData]\n    test_data = [\n        ('a', {'k1': 'v1', 'x1': {'y1': 'z1'}}),\n        ('b', {'k2': 'v2', 'x2': {'y2': 'z2'}}),\n        ('c', {'k1': 'v1', 'k2': 'v2'}),\n        ('d', {'x1': {'y1': 'z1', 'y2': 'z2'}}),\n        ('e', {'l1': [0, 1, 2], 'l2': [1, [3, 3], 7]}),\n    ]\n    M = KeyData\n\n    def setUp(self):\n        super(TestJSONFieldFunctions, self).setUp()\n        KeyData = self.M\n        with self.database.atomic():\n            for key, data in self.test_data:\n                KeyData.create(key=key, data=data)\n\n        self.Q = KeyData.select().order_by(KeyData.key)\n\n    def assertRows(self, where, expected):\n        self.assertEqual([kd.key for kd in self.Q.where(where)], expected)\n\n    def assertData(self, key, expected):\n        KeyData = self.M\n        self.assertEqual(KeyData.get(KeyData.key == key).data, expected)\n\n    def test_json_group_functions(self):\n        KeyData = self.M\n        with self.database.atomic():\n            KeyData.delete().execute()\n            for i in range(10):\n                # e.g., {v: 0, v0: {items: []}}, {v: 2, v2: {items: [0, 1]}}\n                KeyData.create(key='k%s' % i, data={'v': i, 'v%s' % i: {\n                    'items': list(range(i))}})\n\n        jga_key = fn.json_group_array(KeyData.key)\n        query = (KeyData\n                 .select(jga_key)\n                 .where(KeyData.data['v'] < 4)\n                 .order_by(KeyData.key))\n        self.assertEqual(json.loads(query.scalar()), ['k0', 'k1', 'k2', 'k3'])\n\n        # Can specify json.loads as the converter for the function.\n        query = (KeyData\n                 .select(jga_key.python_value(json.loads))\n                 .where(KeyData.data['v'] > 6)\n                 .order_by(KeyData.key))\n        self.assertEqual(query.scalar(), ['k7', 'k8', 'k9'])\n\n        # Aggregating a list of ints?\n        jga_id = fn.json_group_array(KeyData.id)\n        query = (KeyData\n                 .select(jga_id)\n                 .where(KeyData.data['v'] < 4)\n                 .order_by(KeyData.id))\n        self.assertEqual(json.loads(query.scalar()), [1, 2, 3, 4])\n\n        query = (KeyData\n                 .select(jga_id.python_value(json.loads))\n                 .where(KeyData.data['v'] > 6)\n                 .order_by(KeyData.id))\n        self.assertEqual(query.scalar(), [8, 9, 10])\n\n        # Using json_group_object.\n        jgo_key = fn.json_group_object(KeyData.key, KeyData.data['v'])\n        res = (KeyData\n               .select(jgo_key)\n               .where(KeyData.data['v'] < 4)\n               .scalar())\n        self.assertEqual(json.loads(res), {'k0': 0, 'k1': 1, 'k2': 2, 'k3': 3})\n\n        query = (KeyData\n                 .select(jgo_key.python_value(json.loads))\n                 .where(KeyData.data['v'] < 4))\n        self.assertEqual(query.scalar(), {'k0': 0, 'k1': 1, 'k2': 2, 'k3': 3})\n\n    def test_extract(self):\n        KeyData = self.M\n        self.assertRows((KeyData.data['k1'] == 'v1'), ['a', 'c'])\n        self.assertRows((KeyData.data['k2'] == 'v2'), ['b', 'c'])\n        self.assertRows((KeyData.data['x1']['y1'] == 'z1'), ['a', 'd'])\n        self.assertRows((KeyData.data['l1'][1] == 1), ['e'])\n        self.assertRows((KeyData.data['l2'][1][1] == 3), ['e'])\n\n    @skip_unless(json_text_installed())\n    def test_extract_text_json(self):\n        KeyData = self.M\n        D = KeyData.data\n        self.assertRows((D.extract('$.k1') == 'v1'), ['a', 'c'])\n        self.assertRows((D.extract_text('$.k1') == 'v1'), ['a', 'c'])\n        self.assertRows((D.extract_json('$.k1') == '\"v1\"'), ['a', 'c'])\n        self.assertRows((D.extract_text('k2') == 'v2'), ['b', 'c'])\n        self.assertRows((D.extract_json('k2') == '\"v2\"'), ['b', 'c'])\n        self.assertRows((D.extract_text('$.x1.y1') == 'z1'), ['a', 'd'])\n        self.assertRows((D.extract_json('$.x1.y1') == '\"z1\"'), ['a', 'd'])\n        self.assertRows((D.extract_text('$.l1[1]') == 1), ['e'])\n        self.assertRows((D.extract_text('$.l2[1][1]') == 3), ['e'])\n        self.assertRows((D.extract_json('x1') == '{\"y1\":\"z1\"}'), ['a'])\n\n    def test_extract_multiple(self):\n        KeyData = self.M\n        query = KeyData.select(\n            KeyData.key,\n            KeyData.data.extract('$.k1', '$.k2').alias('keys'))\n        self.assertEqual(sorted((k.key, k.keys) for k in query), [\n            ('a', ['v1', None]),\n            ('b', [None, 'v2']),\n            ('c', ['v1', 'v2']),\n            ('d', [None, None]),\n            ('e', [None, None])])\n\n    def test_insert(self):\n        KeyData = self.M\n        # Existing values are not overwritten.\n        query = KeyData.update(data=KeyData.data['k1'].insert('v1-x'))\n        self.assertEqual(query.execute(), 5)\n\n        self.assertData('a', {'k1': 'v1', 'x1': {'y1': 'z1'}})\n        self.assertData('b', {'k1': 'v1-x', 'k2': 'v2', 'x2': {'y2': 'z2'}})\n        self.assertData('c', {'k1': 'v1', 'k2': 'v2'})\n        self.assertData('d', {'k1': 'v1-x', 'x1': {'y1': 'z1', 'y2': 'z2'}})\n        self.assertData('e', {'k1': 'v1-x', 'l1': [0, 1, 2],\n                              'l2': [1, [3, 3], 7]})\n\n    def test_insert_json(self):\n        KeyData = self.M\n        set_json = KeyData.data['k1'].insert([0])\n        query = KeyData.update(data=set_json)\n        self.assertEqual(query.execute(), 5)\n\n        self.assertData('a', {'k1': 'v1', 'x1': {'y1': 'z1'}})\n        self.assertData('b', {'k1': [0], 'k2': 'v2', 'x2': {'y2': 'z2'}})\n        self.assertData('c', {'k1': 'v1', 'k2': 'v2'})\n        self.assertData('d', {'k1': [0], 'x1': {'y1': 'z1', 'y2': 'z2'}})\n        self.assertData('e', {'k1': [0], 'l1': [0, 1, 2],\n                              'l2': [1, [3, 3], 7]})\n\n    def test_replace(self):\n        KeyData = self.M\n        # Only existing values are overwritten.\n        query = KeyData.update(data=KeyData.data['k1'].replace('v1-x'))\n        self.assertEqual(query.execute(), 5)\n\n        self.assertData('a', {'k1': 'v1-x', 'x1': {'y1': 'z1'}})\n        self.assertData('b', {'k2': 'v2', 'x2': {'y2': 'z2'}})\n        self.assertData('c', {'k1': 'v1-x', 'k2': 'v2'})\n        self.assertData('d', {'x1': {'y1': 'z1', 'y2': 'z2'}})\n        self.assertData('e', {'l1': [0, 1, 2], 'l2': [1, [3, 3], 7]})\n\n    def test_replace_json(self):\n        KeyData = self.M\n        set_json = KeyData.data['k1'].replace([0])\n        query = KeyData.update(data=set_json)\n        self.assertEqual(query.execute(), 5)\n\n        self.assertData('a', {'k1': [0], 'x1': {'y1': 'z1'}})\n        self.assertData('b', {'k2': 'v2', 'x2': {'y2': 'z2'}})\n        self.assertData('c', {'k1': [0], 'k2': 'v2'})\n        self.assertData('d', {'x1': {'y1': 'z1', 'y2': 'z2'}})\n        self.assertData('e', {'l1': [0, 1, 2], 'l2': [1, [3, 3], 7]})\n\n    def test_set(self):\n        KeyData = self.M\n        query = (KeyData\n                 .update({KeyData.data: KeyData.data['k1'].set('v1-x')})\n                 .where(KeyData.data['k1'] == 'v1'))\n        self.assertEqual(query.execute(), 2)\n        self.assertRows((KeyData.data['k1'] == 'v1-x'), ['a', 'c'])\n\n        self.assertData('a', {'k1': 'v1-x', 'x1': {'y1': 'z1'}})\n\n    def test_set_json(self):\n        KeyData = self.M\n        set_json = KeyData.data['x1'].set({'y1': 'z1-x', 'y3': 'z3'})\n        query = (KeyData\n                 .update({KeyData.data: set_json})\n                 .where(KeyData.data['x1']['y1'] == 'z1'))\n        self.assertEqual(query.execute(), 2)\n        self.assertRows((KeyData.data['x1']['y1'] == 'z1-x'), ['a', 'd'])\n\n        self.assertData('a', {'k1': 'v1', 'x1': {'y1': 'z1-x', 'y3': 'z3'}})\n        self.assertData('d', {'x1': {'y1': 'z1-x', 'y3': 'z3'}})\n\n    def test_append(self):\n        KeyData = self.M\n        for value in ('ix', [], ['c1'], ['c1', 'c2'], {}, {'k1': 'v1'},\n                      {'k1': 'v1', 'k2': 'v2'}, None, 1):\n            KeyData.delete().execute()\n            KeyData.create(key='a0', data=[])\n            KeyData.create(key='a1', data=['i1'])\n            KeyData.create(key='a2', data=['i1', 'i2'])\n            KeyData.create(key='n0', data={'arr': []})\n            KeyData.create(key='n1', data={'arr': ['i1']})\n            KeyData.create(key='n2', data={'arr': ['i1', 'i2']})\n\n            query = (KeyData\n                     .update(data=KeyData.data.append(value))\n                     .where(KeyData.key.startswith('a')))\n            self.assertEqual(query.execute(), 3)\n\n            query = (KeyData\n                     .select(KeyData.key, fn.json(KeyData.data))\n                     .where(KeyData.key.startswith('a')))\n            self.assertEqual(sorted((row.key, row.data) for row in query),\n                             [('a0', [value]), ('a1', ['i1', value]),\n                              ('a2', ['i1', 'i2', value])])\n\n            query = (KeyData\n                     .update(data=KeyData.data['arr'].append(value))\n                     .where(KeyData.key.startswith('n')))\n            self.assertEqual(query.execute(), 3)\n\n            query = (KeyData\n                     .select(KeyData.key, fn.json(KeyData.data))\n                     .where(KeyData.key.startswith('n')))\n            self.assertEqual(sorted((row.key, row.data) for row in query),\n                             [('n0', {'arr': [value]}),\n                              ('n1', {'arr': ['i1', value]}),\n                              ('n2', {'arr': ['i1', 'i2', value]})])\n\n    @skip_unless(json_patch_installed())\n    def test_update(self):\n        KeyData = self.M\n        merged = KeyData.data.update({'x1': {'y1': 'z1-x', 'y3': 'z3'}})\n        query = (KeyData\n                 .update({KeyData.data: merged})\n                 .where(KeyData.data['x1']['y1'] == 'z1'))\n        self.assertEqual(query.execute(), 2)\n        self.assertRows((KeyData.data['x1']['y1'] == 'z1-x'), ['a', 'd'])\n\n        self.assertData('a', {'k1': 'v1', 'x1': {'y1': 'z1-x', 'y3': 'z3'}})\n        self.assertData('d', {'x1': {'y1': 'z1-x', 'y2': 'z2', 'y3': 'z3'}})\n\n    @skip_unless(json_patch_installed())\n    def test_update_with_removal(self):\n        KeyData = self.M\n        m = KeyData.data.update({'k1': None, 'x1': {'y1': None, 'y3': 'z3'}})\n        query = KeyData.update(data=m).where(KeyData.data['x1']['y1'] == 'z1')\n        self.assertEqual(query.execute(), 2)\n        self.assertRows((KeyData.data['x1']['y3'] == 'z3'), ['a', 'd'])\n\n        self.assertData('a', {'x1': {'y3': 'z3'}})\n        self.assertData('d', {'x1': {'y2': 'z2', 'y3': 'z3'}})\n\n    @skip_unless(json_patch_installed())\n    def test_update_nested(self):\n        KeyData = self.M\n        merged = KeyData.data['x1'].update({'y1': 'z1-x', 'y3': 'z3'})\n        query = (KeyData\n                 .update(data=merged)\n                 .where(KeyData.data['x1']['y1'] == 'z1'))\n        self.assertEqual(query.execute(), 2)\n        self.assertRows((KeyData.data['x1']['y1'] == 'z1-x'), ['a', 'd'])\n\n        self.assertData('a', {'k1': 'v1', 'x1': {'y1': 'z1-x', 'y3': 'z3'}})\n        self.assertData('d', {'x1': {'y1': 'z1-x', 'y2': 'z2', 'y3': 'z3'}})\n\n    @skip_unless(json_patch_installed())\n    def test_updated_nested_with_removal(self):\n        KeyData = self.M\n        merged = KeyData.data['x1'].update({'o1': 'p1', 'y1': None})\n        nrows = (KeyData\n                 .update(data=merged)\n                 .where(KeyData.data['x1']['y1'] == 'z1')\n                 .execute())\n        self.assertRows((KeyData.data['x1']['o1'] == 'p1'), ['a', 'd'])\n        self.assertData('a', {'k1': 'v1', 'x1': {'o1': 'p1'}})\n        self.assertData('d', {'x1': {'o1': 'p1', 'y2': 'z2'}})\n\n    def test_remove(self):\n        KeyData = self.M\n        query = (KeyData\n                 .update(data=KeyData.data['k1'].remove())\n                 .where(KeyData.data['k1'] == 'v1'))\n        self.assertEqual(query.execute(), 2)\n\n        self.assertData('a', {'x1': {'y1': 'z1'}})\n        self.assertData('c', {'k2': 'v2'})\n\n        nrows = (KeyData\n                 .update(data=KeyData.data['l2'][1][1].remove())\n                 .where(KeyData.key == 'e')\n                 .execute())\n        self.assertData('e', {'l1': [0, 1, 2], 'l2': [1, [3], 7]})\n\n    def test_simple_update(self):\n        KeyData = self.M\n        nrows = (KeyData\n                 .update(data={'foo': 'bar'})\n                 .where(KeyData.key.in_(['a', 'b']))\n                 .execute())\n        self.assertData('a', {'foo': 'bar'})\n        self.assertData('b', {'foo': 'bar'})\n\n    def test_children(self):\n        KeyData = self.M\n        children = KeyData.data.children().alias('children')\n        query = (KeyData\n                 .select(KeyData.key, children.c.fullkey.alias('fullkey'))\n                 .from_(KeyData, children)\n                 .where(~children.c.fullkey.contains('k'))\n                 .order_by(KeyData.id, SQL('fullkey')))\n        accum = [(row.key, row.fullkey) for row in query]\n        self.assertEqual(accum, [\n            ('a', '$.x1'),\n            ('b', '$.x2'),\n            ('d', '$.x1'),\n            ('e', '$.l1'), ('e', '$.l2')])\n\n    def test_tree(self):\n        KeyData = self.M\n        tree = KeyData.data.tree().alias('tree')\n        query = (KeyData\n                 .select(tree.c.fullkey.alias('fullkey'))\n                 .from_(KeyData, tree)\n                 .where(KeyData.key == 'd')\n                 .order_by(SQL('1'))\n                 .tuples())\n        self.assertEqual([fullkey for fullkey, in query], [\n            '$',\n            '$.x1',\n            '$.x1.y1',\n            '$.x1.y2'])\n\n\n@skip_unless(jsonb_installed(), 'requires sqlite jsonb support')\nclass TestJSONBFieldFunctions(TestJSONFieldFunctions):\n    requires = [JBData]\n    M = JBData\n\n    def assertData(self, key, expected):\n        q = JBData.select(fn.json(JBData.data)).where(JBData.key == key)\n        self.assertEqual(q.get().data, expected)\n\n    def test_extract_multiple(self):\n        # We need to override this, otherwise we end up with jsonb returned.\n        expr = fn.json(JBData.data.extract('$.k1', '$.k2'))\n        query = JBData.select(\n            JBData.key,\n            expr.python_value(json.loads).alias('keys'))\n        self.assertEqual(sorted((k.key, k.keys) for k in query), [\n            ('a', ['v1', None]),\n            ('b', [None, 'v2']),\n            ('c', ['v1', 'v2']),\n            ('d', [None, None]),\n            ('e', [None, None])])\n\n\nclass TestSqliteExtensions(BaseTestCase):\n    def test_virtual_model(self):\n        class Test(VirtualModel):\n            class Meta:\n                database = database\n                extension_module = 'ext1337'\n                legacy_table_names = False\n                options = {'huey': 'cat', 'mickey': 'dog'}\n                primary_key = False\n\n        class SubTest(Test): pass\n\n        self.assertSQL(Test._schema._create_table(), (\n            'CREATE VIRTUAL TABLE IF NOT EXISTS \"test\" '\n            'USING ext1337 '\n            '(huey=cat, mickey=dog)'), [])\n        self.assertSQL(SubTest._schema._create_table(), (\n            'CREATE VIRTUAL TABLE IF NOT EXISTS \"sub_test\" '\n            'USING ext1337 '\n            '(huey=cat, mickey=dog)'), [])\n        self.assertSQL(\n            Test._schema._create_table(huey='kitten', zaizee='cat'),\n            ('CREATE VIRTUAL TABLE IF NOT EXISTS \"test\" '\n             'USING ext1337 (huey=kitten, mickey=dog, zaizee=cat)'), [])\n\n    def test_autoincrement_field(self):\n        class AutoIncrement(TestModel):\n            id = AutoIncrementField()\n            data = TextField()\n            class Meta:\n                database = database\n\n        self.assertSQL(AutoIncrement._schema._create_table(), (\n            'CREATE TABLE IF NOT EXISTS \"auto_increment\" '\n            '(\"id\" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, '\n            '\"data\" TEXT NOT NULL)'), [])\n\n\nclass BaseFTSTestCase(object):\n    messages = (\n        ('A faith is a necessity to a man. Woe to him who believes in '\n         'nothing.'),\n        ('All who call on God in true faith, earnestly from the heart, will '\n         'certainly be heard, and will receive what they have asked and '\n         'desired.'),\n        ('Be faithful in small things because it is in them that your '\n         'strength lies.'),\n        ('Faith consists in believing when it is beyond the power of reason '\n         'to believe.'),\n        ('Faith has to do with things that are not seen and hope with things '\n         'that are not at hand.'))\n    values = (\n        ('aaaaa bbbbb ccccc ddddd', 'aaaaa ccccc', 'zzzzz zzzzz', 1),\n        ('bbbbb ccccc ddddd eeeee', 'bbbbb', 'zzzzz', 2),\n        ('ccccc ccccc ddddd fffff', 'ccccc', 'yyyyy', 3),\n        ('ddddd', 'ccccc', 'xxxxx', 4))\n\n    def assertMessages(self, query, indexes):\n        self.assertEqual([obj.message for obj in query],\n                         [self.messages[idx] for idx in indexes])\n\n\nclass TestFullTextSearch(BaseFTSTestCase, ModelTestCase):\n    database = database\n    requires = [\n        Post,\n        ContentPost,\n        ContentPostMessage,\n        Document,\n        MultiColumn]\n\n    @requires_models(Document)\n    def test_fts_insert_or_replace(self):\n        # We can use replace to create a new row.\n        n = Document.replace(docid=100, message='m100').execute()\n        self.assertEqual(n, 100)\n        self.assertEqual(Document.select().count(), 1)\n\n        # We can use replace to update an existing row.\n        n = Document.replace(docid=100, message='x100').execute()\n        self.assertEqual(n, 100)\n        self.assertEqual(Document.select().count(), 1)\n\n        # Adds a new row.\n        n = Document.replace(docid=101, message='x101').execute()\n        self.assertEqual(n, 101)\n        self.assertEqual(Document.select().count(), 2)\n\n        query = Document.select().order_by(Document.message)\n        self.assertEqual(list(query.tuples()), [(100, 'x100'), (101, 'x101')])\n\n    @requires_models(Document)\n    def test_fts_manual(self):\n        messages = [Document.create(message=message)\n                    for message in self.messages]\n        query = (Document\n                 .select()\n                 .where(Document.match('believe'))\n                 .order_by(Document.docid))\n        self.assertMessages(query, [0, 3])\n\n        query = Document.search('believe')\n        self.assertMessages(query, [3, 0])\n\n        # Test peewee's \"rank\" algorithm, as presented in the SQLite FTS3 docs.\n        query = Document.search('things', with_score=True)\n        self.assertEqual([(row.message, row.score) for row in query], [\n            (self.messages[4], -2. / 3),\n            (self.messages[2], -1. / 3)])\n\n        # Test peewee's bm25 ranking algorithm.\n        query = Document.search_bm25('things', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[4], -0.45),\n            (self.messages[2], -0.36)])\n\n        # Another test of bm25 ranking.\n        query = Document.search_bm25('believe', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[3], -0.49),\n            (self.messages[0], -0.35)])\n\n        query = Document.search_bm25('god faith', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[1], -0.92)])\n\n        query = Document.search_bm25('\"it is\"', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[2], -0.36),\n            (self.messages[3], -0.36)])\n\n    def test_fts_delete_row(self):\n        posts = [Post.create(message=msg) for msg in self.messages]\n        ContentPost.rebuild()\n        query = (ContentPost\n                 .select(ContentPost, ContentPost.rank().alias('score'))\n                 .where(ContentPost.match('believe'))\n                 .order_by(ContentPost.docid))\n        self.assertMessages(query, [0, 3])\n\n        query = (ContentPost\n                 .select(ContentPost.docid)\n                 .order_by(ContentPost.docid))\n        for content_post in query:\n            self.assertEqual(content_post.delete_instance(), 1)\n\n        for post in posts:\n            self.assertEqual(\n                (ContentPost\n                 .delete()\n                 .where(ContentPost.message == post.message)\n                 .execute()), 1)\n\n        # None of the deletes were processed since the table is managed.\n        self.assertEqual(ContentPost.select().count(), 5)\n\n        documents = [Document.create(message=message) for message in\n                     self.messages]\n        self.assertEqual(Document.select().count(), 5)\n\n        for document in documents:\n            self.assertEqual(\n                (Document\n                 .delete()\n                 .where(Document.message == document.message)\n                 .execute()), 1)\n\n        self.assertEqual(Document.select().count(), 0)\n\n    def _create_multi_column(self):\n        for c1, c2, c3, c4 in self.values:\n            MultiColumn.create(c1=c1, c2=c2, c3=c3, c4=c4)\n\n    @requires_models(MultiColumn)\n    def test_fts_multi_column(self):\n        def assertResults(term, expected):\n            results = [(x.c4, round(x.score, 2))\n                       for x in MultiColumn.search(term, with_score=True)]\n            self.assertEqual(results, expected)\n\n        self._create_multi_column()\n        assertResults('bbbbb', [\n            (2, -1.5),  # 1/2 + 1/1\n            (1, -0.5)])  # 1/2\n\n        # `ccccc` appears four times in `c1`, three times in `c2`.\n        assertResults('ccccc', [\n            (3, -.83),  # 2/4 + 1/3\n            (1, -.58), # 1/4 + 1/3\n            (4, -.33), # 1/3\n            (2, -.25), # 1/4\n        ])\n\n        # `zzzzz` appears three times in c3.\n        assertResults('zzzzz', [(1, -.67), (2, -.33)])\n\n        self.assertEqual(\n            [x.score for x in MultiColumn.search('ddddd', with_score=True)],\n            [-.25, -.25, -.25, -.25])\n\n    @requires_models(MultiColumn)\n    def test_bm25(self):\n        def assertResults(term, expected):\n            query = MultiColumn.search_bm25(term, [1.0, 0, 0, 0], True)\n            self.assertEqual(\n                [(mc.c4, round(mc.score, 2)) for mc in query],\n                expected)\n\n        self._create_multi_column()\n        MultiColumn.create(c1='aaaaa fffff', c4=5)\n\n        assertResults('aaaaa', [(5, -0.39), (1, -0.3)])\n        assertResults('fffff', [(5, -0.39), (3, -0.3)])\n        assertResults('eeeee', [(2, -0.97)])\n\n        # No column specified, use the first text field.\n        query = MultiColumn.search_bm25('fffff', [1.0, 0, 0, 0], True)\n        self.assertEqual([(mc.c4, round(mc.score, 2)) for mc in query], [\n            (5, -0.39),\n            (3, -0.3)])\n\n        # Use helpers.\n        query = (MultiColumn\n                 .select(\n                     MultiColumn.c4,\n                     MultiColumn.bm25(1.0).alias('score'))\n                 .where(MultiColumn.match('aaaaa'))\n                 .order_by(SQL('score')))\n        self.assertEqual([(mc.c4, round(mc.score, 2)) for mc in query], [\n            (5, -0.39),\n            (1, -0.3)])\n\n        def assertAllColumns(term, expected):\n            query = MultiColumn.search_bm25(term, with_score=True)\n            self.assertEqual(\n                [(mc.c4, round(mc.score, 2)) for mc in query],\n                expected)\n\n        assertAllColumns('aaaaa ddddd', [(1, -1.08)])\n        assertAllColumns('zzzzz ddddd', [(1, -0.36), (2, -0.34)])\n        assertAllColumns('ccccc bbbbb ddddd', [(2, -1.39), (1, -0.3)])\n\n    @requires_models(Document)\n    def test_bm25_alt_corpus(self):\n        for message in self.messages:\n            Document.create(message=message)\n\n        query = Document.search_bm25('things', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[4], -0.45),\n            (self.messages[2], -0.36)])\n\n        query = Document.search_bm25('believe', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[3], -0.49),\n            (self.messages[0], -0.35)])\n\n        # Indeterminate order since all are 0.0. All phrases contain the word\n        # faith, so there is no meaningful score.\n        query = Document.search_bm25('faith', with_score=True)\n        self.assertEqual([round(d.score, 2) for d in query], [-0.] * 5)\n\n    def _test_fts_auto(self, ModelClass):\n        posts = []\n        for message in self.messages:\n            posts.append(Post.create(message=message))\n\n        # Nothing matches, index is not built.\n        pq = ModelClass.select().where(ModelClass.match('faith'))\n        self.assertEqual(list(pq), [])\n\n        ModelClass.rebuild()\n        ModelClass.optimize()\n\n        # it will stem faithful -> faith b/c we use the porter tokenizer\n        pq = (ModelClass\n              .select()\n              .where(ModelClass.match('faith'))\n              .order_by(ModelClass.docid))\n        self.assertMessages(pq, range(len(self.messages)))\n\n        pq = (ModelClass\n              .select()\n              .where(ModelClass.match('believe'))\n              .order_by(ModelClass.docid))\n        self.assertMessages(pq, [0, 3])\n\n        pq = (ModelClass\n              .select()\n              .where(ModelClass.match('thin*'))\n              .order_by(ModelClass.docid))\n        self.assertMessages(pq, [2, 4])\n\n        pq = (ModelClass\n              .select()\n              .where(ModelClass.match('\"it is\"'))\n              .order_by(ModelClass.docid))\n        self.assertMessages(pq, [2, 3])\n\n        pq = ModelClass.search('things', with_score=True)\n        self.assertEqual([(x.message, x.score) for x in pq], [\n            (self.messages[4], -2.0 / 3),\n            (self.messages[2], -1.0 / 3),\n        ])\n\n        pq = (ModelClass\n              .select(ModelClass.rank())\n              .where(ModelClass.match('faithful'))\n              .tuples())\n        self.assertEqual([x[0] for x in pq], [-.2] * 5)\n\n        pq = (ModelClass\n              .search('faithful', with_score=True)\n              .dicts())\n        self.assertEqual([x['score'] for x in pq], [-.2] * 5)\n\n    def test_fts_auto_model(self):\n        self._test_fts_auto(ContentPost)\n\n    def test_fts_auto_field(self):\n        self._test_fts_auto(ContentPostMessage)\n\n    def test_weighting(self):\n        self._create_multi_column()\n        def assertResults(method, term, weights, expected):\n            results = [\n                (x.c4, round(x.score, 2))\n                for x in method(term, weights=weights, with_score=True)]\n            self.assertEqual(results, expected)\n\n        assertResults(MultiColumn.search, 'bbbbb', None, [\n            (2, -1.5),  # 1/2 + 1/1\n            (1, -0.5),  # 1/2\n        ])\n        assertResults(MultiColumn.search, 'bbbbb', [1., 5., 0.], [\n            (2, -5.5),  # 1/2 + (5 * 1/1)\n            (1, -0.5),  # 1/2 + (5 * 0)\n        ])\n        assertResults(MultiColumn.search, 'bbbbb', [1., .5, 0.], [\n            (2, -1.),  # 1/2 + (.5 * 1/1)\n            (1, -0.5),  # 1/2 + (.5 * 0)\n        ])\n        assertResults(MultiColumn.search, 'bbbbb', [1., -1., 0.], [\n            (1, -0.5),  # 1/2 + (-1 * 0)\n            (2, 0.5),  # 1/2 + (-1 * 1/1)\n        ])\n\n        # BM25\n        assertResults(MultiColumn.search_bm25, 'bbbbb', None, [\n            (2, -0.85),\n            (1, -0.)])\n        assertResults(MultiColumn.search_bm25, 'bbbbb', [1., 5., 0.], [\n            (2, -4.24),\n            (1, -0.)])\n        assertResults(MultiColumn.search_bm25, 'bbbbb', [1., .5, 0.], [\n            (2, -0.42),\n            (1, -0.)])\n        assertResults(MultiColumn.search_bm25, 'bbbbb', [1., -1., 0.], [\n            (1, -0.),\n            (2, 0.85)])\n\n    def test_fts_match_single_column(self):\n        data = (\n            ('m1c1 aaaa', 'm1c2 bbbb', 'm1c3 cccc'),\n            ('m2c1 dddd', 'm2c2 eeee', 'm2c3 ffff'),\n            ('m3c1 cccc', 'm3c2 bbbb', 'm3c3 aaaa'),\n        )\n        for c1, c2, c3 in data:\n            MultiColumn.create(c1=c1, c2=c2, c3=c3, c4=0)\n\n        def assertSearch(field, value, expected):\n            query = (MultiColumn\n                     .select()\n                     .where(field.match(value))\n                     .order_by(MultiColumn.c1))\n            self.assertEqual([mc.c1[:2] for mc in query], expected)\n\n        assertSearch(MultiColumn.c1, 'aaaa', ['m1'])\n        assertSearch(MultiColumn.c1, 'bbbb', [])\n        assertSearch(MultiColumn.c1, 'cccc', ['m3'])\n        assertSearch(MultiColumn.c2, 'bbbb', ['m1', 'm3'])\n        assertSearch(MultiColumn.c2, 'eeee', ['m2'])\n        assertSearch(MultiColumn.c3, 'cccc', ['m1'])\n        assertSearch(MultiColumn.c3, 'aaaa', ['m3'])\n\n    def test_fts_score_single_column(self):\n        data = (\n            ('m1c1 aaaa', 'm1c2 bbbb', 'm1c3 cccc'),\n            ('m2c1 dddd', 'm2c2 eeee', 'm2c3 ffff'),\n            ('m3c1 cccc', 'm3c2 bbbb aaaa', 'm3c3 aaaa aaaa'),\n        )\n        for c1, c2, c3 in data:\n            MultiColumn.create(c1=c1, c2=c2, c3=c3, c4=0)\n\n        def assertQueryScore(field, search_term, expected, *weights):\n            rank = MultiColumn.bm25(*weights)\n            query = (MultiColumn\n                     .select(MultiColumn, rank.alias('score'))\n                     .where(field.match(search_term))\n                     .order_by(rank))\n            results = [(r.c1[:2], round(r.score, 2)) for r in query]\n            self.assertEqual(results, expected)\n\n        assertQueryScore(MultiColumn.c1, 'aaaa', [('m1', -0.51)])\n        assertQueryScore(MultiColumn.c1, 'dddd', [('m2', -0.51)])\n        assertQueryScore(MultiColumn.c2, 'bbbb', [('m1', -0.), ('m3', -0.)])\n        assertQueryScore(MultiColumn.c2, 'eeee', [('m2', -0.51)])\n        assertQueryScore(MultiColumn.c3, 'aaaa', [('m3', -0.62)])\n\n        assertQueryScore(MultiColumn.c1, 'aaaa', [('m1', -1.02)], 2., 0., 0.)\n        assertQueryScore(MultiColumn.c2, 'bbbb', [('m1', -0.), ('m3', -0.)],\n                         0., 1.0, 0.)\n        assertQueryScore(MultiColumn.c2, 'eeee', [('m2', -1.02)], 0., 2., 0.)\n        assertQueryScore(MultiColumn.c3, 'aaaa', [('m3', -0.31)], 0., 1., 0.5)\n\n    @skip_unless(compile_option('enable_fts4'))\n    @requires_models(MultiColumn)\n    def test_match_column_queries(self):\n        data = (\n            ('alpha one', 'apple aspires to ace artsy beta launch'),\n            ('beta two', 'beta boasts better broadcast over apple'),\n            ('gamma three', 'gold gray green gamma ray delta data'),\n            ('delta four', 'delta data indicates downturn for apple beta'),\n        )\n        MC = MultiColumn\n\n        for i, (title, message) in enumerate(data):\n            MC.create(c1=title, c2=message, c3='', c4=i)\n\n        def assertQ(expr, idxscore):\n            q = (MC\n                 .select(MC, MC.bm25().alias('score'))\n                 .where(expr)\n                 .order_by(SQL('score'), MC.c4))\n            self.assertEqual([(r.c4, round(r.score, 2)) for r in q], idxscore)\n\n        # Single whitespace does not affect the mapping of col->term. We can\n        # also store the column value in quotes if single-quotes are used.\n        assertQ(MC.match('beta'), [(1, -0.85), (0, -0.), (3, -0.)])\n        assertQ(MC.match('c1:beta'), [(1, -0.85)])\n        assertQ(MC.match('c1: beta'), [(1, -0.85)])\n        assertQ(MC.match('c1: ^bet*'), [(1, -0.85)])\n        assertQ(MC.match('c1: \\'beta\\''), [(1, -0.85)])\n        assertQ(MC.match('\"beta\"'), [(1, -0.85), (0, -0.), (3, -0.)])\n\n        # Alternatively, just specify the column explicitly.\n        assertQ(MC.c1.match('beta'), [(1, -0.85)])\n        assertQ(MC.c1.match(' beta '), [(1, -0.85)])\n        assertQ(MC.c1.match('\"beta\"'), [(1, -0.85)])\n        assertQ(MC.c1.match('\"^bet*\"'), [(1, -0.85)])\n\n        #                 apple   beta   delta   gamma\n        # 0  |  alpha  |    X       X\n        # 1  |  beta   |    X       X\n        # 2  |  gamma  |                   X       X\n        # 3  |  delta  |    X       X      X\n        #\n        assertQ(MC.match('delta NOT gamma'), [(3, -0.85)])\n        assertQ(MC.match('delta NOT c2:gamma'), [(3, -0.85)])\n        assertQ(MC.match('\"delta\"'), [(3, -0.85), (2, -0.)])\n        assertQ(MC.match('c1:delta OR c2:delta'), [(3, -0.85), (2, -0.)])\n        assertQ(MC.match('\"^delta\"'), [(3, -1.69)])\n\n        assertQ(MC.match('(delta AND c2:apple) OR c1:alpha'),\n                [(3, -0.85), (0, -0.85)])\n        assertQ(MC.match('(c2:delta AND c2:apple) OR c1:alpha'),\n                [(0, -0.85), (3, -0.)])\n        assertQ(MC.match('c2:delta c2:apple OR c1:alpha'),\n                [(0, -0.85), (3, -0.)])\n        assertQ(MC.match('(c2:delta AND c2:apple) OR beta'),\n                [(1, -0.85), (3, -0.), (0, -0.)])\n        assertQ(MC.match('c2:delta AND (c2:apple OR c1:alpha)'),\n                [(3, -0.)])\n\n        # c2 apple (0,1,3) OR (...irrelevant...).\n        assertQ(MC.match('c2:apple OR c1:alpha NOT delta'),\n                [(0, -0.85), (1, -0.), (3, -0.)])\n        assertQ(MC.match('c2:apple OR (c1:alpha NOT c2:delta)'),\n                [(0, -0.85), (1, -0.), (3, -0.)])\n        # c2 apple OR c1 alpha (0, 1, 3) AND NOT delta (2, 3) -> (0, 1).\n        assertQ(MC.match('(c2:apple OR c1:alpha) NOT delta'),\n                [(0, -0.85), (1, -0.)])\n\n\n@skip_unless(CYTHON_EXTENSION, 'requires _sqlite_udf c extension')\nclass TestFullTextSearchCython(TestFullTextSearch):\n    def test_bm25f(self):\n        def assertResults(term, expected):\n            query = MultiColumn.search_bm25f(term, [1.0, 0, 0, 0], True)\n            self.assertEqual(\n                [(mc.c4, round(mc.score, 2)) for mc in query],\n                expected)\n\n        self._create_multi_column()\n        MultiColumn.create(c1='aaaaa fffff', c4=5)\n\n        assertResults('aaaaa', [(5, -0.76), (1, -0.62)])\n        assertResults('fffff', [(5, -0.76), (3, -0.65)])\n        assertResults('eeeee', [(2, -2.13)])\n\n        # No column specified, use the first text field.\n        query = MultiColumn.search_bm25f('aaaaa OR fffff', [1., 3., 0, 0], 1)\n        self.assertEqual([(mc.c4, round(mc.score, 2)) for mc in query], [\n            (1, -14.18),\n            (5, -12.01),\n            (3, -11.48)])\n\n    def test_lucene(self):\n        for message in self.messages:\n            Document.create(message=message)\n\n        def assertResults(term, expected, sort_cleaned=False):\n            query = Document.search_lucene(term, with_score=True)\n            cleaned = [\n                (round(doc.score, 3), ' '.join(doc.message.split()[:2]))\n                for doc in query]\n            if sort_cleaned:\n                cleaned = sorted(cleaned)\n            self.assertEqual(cleaned, expected)\n\n        assertResults('things', [\n            (-0.166, 'Faith has'),\n            (-0.137, 'Be faithful')])\n\n        assertResults('faith', [\n            (0.036, 'All who'),\n            (0.042, 'Faith has'),\n            (0.047, 'A faith'),\n            (0.049, 'Be faithful'),\n            (0.049, 'Faith consists')], sort_cleaned=True)\n\n\n@skip_unless(FTS5Model.fts5_installed(), 'requires fts5')\nclass TestFTS5(BaseFTSTestCase, ModelTestCase):\n    database = database\n    requires = [FTS5Test]\n    test_corpus = (\n        ('foo aa bb', 'aa bb cc ' * 10, 1),\n        ('bar bb cc', 'bb cc dd ' * 9, 2),\n        ('baze cc dd', 'cc dd ee ' * 8, 3),\n        ('nug aa dd', 'bb cc ' * 7, 4))\n\n    def setUp(self):\n        super(TestFTS5, self).setUp()\n        for title, data, misc in self.test_corpus:\n            FTS5Test.create(title=title, data=data, misc=misc)\n\n    def test_create_table(self):\n        query = FTS5Test._schema._create_table()\n        self.assertSQL(query, (\n            'CREATE VIRTUAL TABLE IF NOT EXISTS \"fts5_test\" USING fts5 '\n            '(\"title\", \"data\", \"misc\" UNINDEXED)'), [])\n\n    def test_custom_fts5_command(self):\n        merge_sql = FTS5Test._fts_cmd_sql('merge', rank=4)\n        self.assertSQL(merge_sql, (\n            'INSERT INTO \"fts5_test\" (\"fts5_test\", \"rank\") VALUES (?, ?)'),\n            ['merge', 4])\n        FTS5Test.merge(4)  # Runs without error.\n\n        FTS5Test.insert_many([{'title': 'k%08d' % i, 'data': 'v%08d' % i}\n                              for i in range(100)]).execute()\n\n        FTS5Test.integrity_check(rank=0)\n        FTS5Test.optimize()\n\n    def test_create_table_options(self):\n        class Test1(FTS5Model):\n            f1 = SearchField()\n            f2 = SearchField(unindexed=True)\n            f3 = SearchField()\n\n            class Meta:\n                database = self.database\n                options = {\n                    'prefix': (2, 3),\n                    'tokenize': 'porter unicode61',\n                    'content': Post,\n                    'content_rowid': Post.id}\n\n        query = Test1._schema._create_table()\n        self.assertSQL(query, (\n            'CREATE VIRTUAL TABLE IF NOT EXISTS \"test1\" USING fts5 ('\n            '\"f1\", \"f2\" UNINDEXED, \"f3\", '\n            'content=\"post\", content_rowid=\"id\", '\n            'prefix=\\'2,3\\', tokenize=\"porter unicode61\")'), [])\n\n    def assertResults(self, query, expected, scores=False, alias='score'):\n        if scores:\n            results = [(obj.title, round(getattr(obj, alias), 7))\n                       for obj in query]\n        else:\n            results = [obj.title for obj in query]\n        self.assertEqual(results, expected)\n\n    def test_search(self):\n        query = FTS5Test.search('bb')\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"rowid\", \"t1\".\"title\", \"t1\".\"data\", \"t1\".\"misc\" '\n            'FROM \"fts5_test\" AS \"t1\" '\n            'WHERE (\"fts5_test\" MATCH ?) ORDER BY rank'), ['bb'])\n        self.assertResults(query, ['nug aa dd', 'foo aa bb', 'bar bb cc'])\n\n        self.assertResults(FTS5Test.search('baze OR dd'),\n                           ['baze cc dd', 'bar bb cc', 'nug aa dd'])\n\n    @requires_models(FTS5Document)\n    def test_fts_manual(self):\n        messages = [FTS5Document.create(message=message)\n                    for message in self.messages]\n        query = (FTS5Document\n                 .select()\n                 .where(FTS5Document.match('believe'))\n                 .order_by(FTS5Document.rowid))\n        self.assertMessages(query, [0, 3])\n\n        query = FTS5Document.search('believe')\n        self.assertMessages(query, [3, 0])\n\n        # Test SQLite's built-in ranking algorithm (bm25). The results should\n        # be comparable to our user-defined implementation.\n        query = FTS5Document.search('things', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[4], -0.45),\n            (self.messages[2], -0.37)])\n\n        # Another test of bm25 ranking.\n        query = FTS5Document.search_bm25('believe', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[3], -0.49),\n            (self.messages[0], -0.36)])\n\n        query = FTS5Document.search_bm25('god faith', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[1], -0.93)])\n\n        query = FTS5Document.search_bm25('\"it is\"', with_score=True)\n        self.assertEqual([(d.message, round(d.score, 2)) for d in query], [\n            (self.messages[2], -0.37),\n            (self.messages[3], -0.37)])\n\n    def test_match_column_queries(self):\n        data = (\n            ('alpha one', 'apple aspires to ace artsy beta launch'),\n            ('beta two', 'beta boasts better broadcast over apple'),\n            ('gamma three', 'gold gray green gamma ray delta data'),\n            ('delta four', 'delta data indicates downturn for apple beta'),\n        )\n        FT = FTS5Test\n\n        for i, (title, message) in enumerate(data):\n            FT.create(title=title, data=message, misc=str(i))\n\n        def assertQ(expr, idxscore):\n            q = (FT\n                 .select(FT, FT.bm25().alias('score'))\n                 .where(expr)\n                 .order_by(SQL('score'), FT.misc.cast('int')))\n            self.assertEqual([(int(r.misc), round(r.score, 2)) for r in q],\n                             idxscore)\n\n        # Single whitespace does not affect the mapping of col->term. We can\n        # also store the column value in quotes if single-quotes are used.\n        assertQ(FT.match('beta'), [(1, -0.74), (0, -0.57), (3, -0.57)])\n        assertQ(FT.match('title: beta'), [(1, -2.08)])\n        assertQ(FT.match('title: ^bet*'), [(1, -2.08)])\n        assertQ(FT.match('title: \"beta\"'), [(1, -2.08)])\n        assertQ(FT.match('\"beta\"'), [(1, -0.74), (0, -0.57), (3, -0.57)])\n\n        # Alternatively, just specify the column explicitly.\n        assertQ(FT.title.match('beta'), [(1, -2.08)])\n        assertQ(FT.title.match(' beta '), [(1, -2.08)])\n        assertQ(FT.title.match('\"beta\"'), [(1, -2.08)])\n        assertQ(FT.title.match('^bet*'), [(1, -2.08)])\n        assertQ(FT.title.match('\"^bet*\"'), [])  # No wildcards in quotes!\n\n        #                 apple   beta   delta   gamma\n        # 0  |  alpha  |    X       X\n        # 1  |  beta   |    X       X\n        # 2  |  gamma  |                   X       X\n        # 3  |  delta  |    X       X      X\n        #\n        assertQ(FT.match('delta NOT gamma'), [(3, -1.53)])\n        assertQ(FT.match('delta NOT data:gamma'), [(3, -1.53)])\n        assertQ(FT.match('\"delta\"'), [(3, -1.53), (2, -1.2)])\n        assertQ(FT.match('title:delta OR data:delta'), [(3, -3.21), (2, -1.2)])\n        assertQ(FT.match('\"^delta\"'), [(3, -1.53), (2, -1.2)])  # Different.\n        assertQ(FT.match('^delta'), [(3, -2.57)])  # Different from FTS4.\n\n        assertQ(FT.match('(delta AND data:apple) OR title:alpha'),\n                [(3, -2.09), (0, -2.02)])\n        assertQ(FT.match('(data:delta AND data:apple) OR title:alpha'),\n                [(0, -2.02), (3, -1.76)])\n        assertQ(FT.match('data:delta data:apple OR title:alpha'),\n                [(0, -2.02), (3, -1.76)])\n        assertQ(FT.match('(data:delta AND data:apple) OR beta'),\n                [(3, -2.33), (1, -0.74), (0, -0.57)])\n        assertQ(FT.match('data:delta AND (data:apple OR title:alpha)'),\n                [(3, -1.76)])\n\n        # data apple (0,1,3) OR (...irrelevant...).\n        assertQ(FT.match('data:apple OR title:alpha NOT delta'),\n                [(0, -2.58), (1, -0.58), (3, -0.57)])\n        assertQ(FT.match('data:apple OR (title:alpha NOT data:delta)'),\n                [(0, -2.58), (1, -0.58), (3, -0.57)])\n        # data apple OR title alpha (0, 1, 3) AND NOT delta (2, 3) -> (0, 1).\n        assertQ(FT.match('(data:apple OR title:alpha) NOT delta'),\n                [(0, -2.58), (1, -0.58)])\n\n    def test_highlight_function(self):\n        query = (FTS5Test\n                 .search('dd')\n                 .select(FTS5Test.title.highlight('[', ']').alias('hi')))\n        accum = [row.hi for row in query]\n        self.assertEqual(accum, ['baze cc [dd]', 'bar bb cc', 'nug aa [dd]'])\n\n        query = (FTS5Test\n                 .search('bb')\n                 .select(FTS5Test.data.highlight('[', ']').alias('hi')))\n        accum = [row.hi[:7] for row in query]\n        self.assertEqual(accum, ['[bb] cc', 'aa [bb]', '[bb] cc'])\n\n    def test_snippet_function(self):\n        snip = FTS5Test.data.snippet('[', ']', max_tokens=5).alias('snip')\n        query = FTS5Test.search('dd').select(snip)\n        accum = [row.snip for row in query]\n        self.assertEqual(accum, [\n            'cc [dd] ee cc [dd]...',\n            'bb cc [dd] bb cc...',\n            'bb cc bb cc bb...'])\n\n    def test_clean_query(self):\n        cases = (\n            ('test', 'test'),\n            ('\"test\"', '\"test\"'),\n            ('\"test\\u2022\"', '\"test\\u2022\"'),\n            ('test\\u2022', 'test\\u2022'),\n            ('test-', 'test\\x1a'),\n            ('\"test-\"', '\"test-\"'),\n            ('\\\\\"test-', '\\x1a test\\x1a'),\n            ('--test--', '\\x1a\\x1atest\\x1a\\x1a'),\n            ('-test- \"-test-\"', '\\x1atest\\x1a \"-test-\"'),\n        )\n        for a, b in cases:\n            self.assertEqual(FTS5Test.clean_query(a), b)\n\n\nclass TestUserDefinedCallbacks(ModelTestCase):\n    database = database\n    requires = [Post, Values]\n\n    def test_custom_agg(self):\n        data = (\n            (1, 3.4, 1.0),\n            (1, 6.4, 2.3),\n            (1, 4.3, 0.9),\n            (2, 3.4, 1.4),\n            (3, 2.7, 1.1),\n            (3, 2.5, 1.1),\n        )\n        for klass, value, wt in data:\n            Values.create(klass=klass, value=value, weight=wt)\n\n        vq = (Values\n              .select(\n                  Values.klass,\n                  fn.weighted_avg(Values.value).alias('wtavg'),\n                  fn.avg(Values.value).alias('avg'))\n              .group_by(Values.klass))\n        q_data = [(v.klass, v.wtavg, v.avg) for v in vq]\n        self.assertEqual(q_data, [\n            (1, 4.7, 4.7),\n            (2, 3.4, 3.4),\n            (3, 2.6, 2.6)])\n\n        vq = (Values\n              .select(\n                  Values.klass,\n                  fn.weighted_avg2(Values.value, Values.weight).alias('wtavg'),\n                  fn.avg(Values.value).alias('avg'))\n              .group_by(Values.klass))\n        q_data = [(v.klass, str(v.wtavg)[:4], v.avg) for v in vq]\n        self.assertEqual(q_data, [\n            (1, '5.23', 4.7),\n            (2, '3.4', 3.4),\n            (3, '2.6', 2.6)])\n\n    def test_custom_collation(self):\n        for i in [1, 4, 3, 5, 2]:\n            Post.create(message='p%d' % i)\n\n        pq = Post.select().order_by(NodeList((Post.message, SQL('collate collate_reverse'))))\n        self.assertEqual([p.message for p in pq], ['p5', 'p4', 'p3', 'p2', 'p1'])\n\n    def test_collation_decorator(self):\n        posts = [Post.create(message=m) for m in ['aaa', 'Aab', 'ccc', 'Bba', 'BbB']]\n        pq = Post.select().order_by(collate_case_insensitive.collation(Post.message))\n        self.assertEqual([p.message for p in pq], [\n            'aaa',\n            'Aab',\n            'Bba',\n            'BbB',\n            'ccc'])\n\n    def test_custom_function(self):\n        p1 = Post.create(message='this is a test')\n        p2 = Post.create(message='another TEST')\n\n        sq = Post.select().where(fn.title_case(Post.message) == 'This Is A Test')\n        self.assertEqual(list(sq), [p1])\n\n        sq = Post.select(fn.title_case(Post.message)).tuples()\n        self.assertEqual([x[0] for x in sq], [\n            'This Is A Test',\n            'Another Test',\n        ])\n\n    def test_function_decorator(self):\n        [Post.create(message=m) for m in ['testing', 'chatting  ', '  foo']]\n        pq = Post.select(fn.rstrip(Post.message, 'ing')).order_by(Post.id)\n        self.assertEqual([x[0] for x in pq.tuples()], [\n            'test', 'chatting  ', '  foo'])\n\n        pq = Post.select(fn.rstrip(Post.message, ' ')).order_by(Post.id)\n        self.assertEqual([x[0] for x in pq.tuples()], [\n            'testing', 'chatting', '  foo'])\n\n    def test_use_across_connections(self):\n        db = get_in_memory_db()\n        @db.func()\n        def rev(s):\n            return s[::-1]\n\n        db.connect(); db.close(); db.connect()\n        curs = db.execute_sql('select rev(?)', ('hello',))\n        self.assertEqual(curs.fetchone(), ('olleh',))\n\n\nclass TestRowIDField(ModelTestCase):\n    database = database\n    requires = [RowIDModel]\n\n    def test_model_meta(self):\n        self.assertEqual(RowIDModel._meta.sorted_field_names, ['rowid', 'data'])\n        self.assertEqual(RowIDModel._meta.primary_key.name, 'rowid')\n        self.assertTrue(RowIDModel._meta.auto_increment)\n\n    def test_rowid_field(self):\n        r1 = RowIDModel.create(data=10)\n        self.assertEqual(r1.rowid, 1)\n        self.assertEqual(r1.data, 10)\n\n        r2 = RowIDModel.create(data=20)\n        self.assertEqual(r2.rowid, 2)\n        self.assertEqual(r2.data, 20)\n\n        query = RowIDModel.select().where(RowIDModel.rowid == 2)\n        self.assertSQL(query, (\n            'SELECT \"t1\".\"rowid\", \"t1\".\"data\" '\n            'FROM \"row_id_model\" AS \"t1\" '\n            'WHERE (\"t1\".\"rowid\" = ?)'), [2])\n        r_db = query.get()\n        self.assertEqual(r_db.rowid, 2)\n        self.assertEqual(r_db.data, 20)\n\n        r_db2 = query.columns(RowIDModel.rowid, RowIDModel.data).get()\n        self.assertEqual(r_db2.rowid, 2)\n        self.assertEqual(r_db2.data, 20)\n\n    def test_insert_with_rowid(self):\n        RowIDModel.insert({RowIDModel.rowid: 5, RowIDModel.data: 1}).execute()\n        self.assertEqual(5, RowIDModel.select(RowIDModel.rowid).first().rowid)\n\n    def test_insert_many_with_rowid_without_field_validation(self):\n        RowIDModel.insert_many([{RowIDModel.rowid: 5, RowIDModel.data: 1}]).execute()\n        self.assertEqual(5, RowIDModel.select(RowIDModel.rowid).first().rowid)\n\n    def test_insert_many_with_rowid_with_field_validation(self):\n        RowIDModel.insert_many([{RowIDModel.rowid: 5, RowIDModel.data: 1}]).execute()\n        self.assertEqual(5, RowIDModel.select(RowIDModel.rowid).first().rowid)\n\n\nclass CalendarMonth(TestModel):\n    name = TextField()\n    value = IntegerField()\n\nclass CalendarDay(TestModel):\n    month = ForeignKeyField(CalendarMonth, backref='days')\n    value = IntegerField()\n\n\nclass TestIntWhereChain(ModelTestCase):\n    database = database\n    requires = [CalendarMonth, CalendarDay]\n\n    def test_int_where_chain(self):\n        with self.database.atomic():\n            jan = CalendarMonth.create(name='january', value=1)\n            feb = CalendarMonth.create(name='february', value=2)\n            CalendarDay.insert_many([{'month': jan, 'value': i + 1}\n                                     for i in range(31)]).execute()\n            CalendarDay.insert_many([{'month': feb, 'value': i + 1}\n                                     for i in range(28)]).execute()\n\n        def assertValues(query, expected):\n            self.assertEqual(sorted([d.value for d in query]), list(expected))\n\n        q = CalendarDay.select().join(CalendarMonth)\n        jq = q.where(CalendarMonth.name == 'january')\n        jq1 = jq.where(CalendarDay.value >= 25)\n        assertValues(jq1, range(25, 32))\n\n        jq2 = jq1.where(CalendarDay.value < 30)\n        assertValues(jq2, range(25, 30))\n\n        fq = q.where(CalendarMonth.name == 'february')\n        fq1 = fq.where(CalendarDay.value >= 25)\n        assertValues(fq1, range(25, 29))\n\n        fq2 = fq1.where(CalendarDay.value < 30)\n        assertValues(fq2, range(25, 29))\n\n\nclass Datum(TestModel):\n    a = BareField()\n    b = BareField(collation='BINARY')\n    c = BareField(collation='RTRIM')\n    d = BareField(collation='NOCASE')\n\n\nclass TestCollatedFieldDefinitions(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [Datum]\n\n    def test_collated_fields(self):\n        rows = (\n            (1, 'abc', 'abc',  'abc  ', 'abc'),\n            (2, 'abc', 'abc',  'abc',   'ABC'),\n            (3, 'abc', 'abc',  'abc ',  'Abc'),\n            (4, 'abc', 'abc ', 'ABC',   'abc'))\n        for pk, a, b, c, d in rows:\n            Datum.create(id=pk, a=a, b=b, c=c, d=d)\n\n        def assertC(query, expected):\n            self.assertEqual([r.id for r in query], expected)\n\n        base = Datum.select().order_by(Datum.id)\n\n        # Text comparison a=b is performed using binary collating sequence.\n        assertC(base.where(Datum.a == Datum.b), [1, 2, 3])\n\n        # Text comparison a=b is performed using the RTRIM collating sequence.\n        assertC(base.where(Datum.a == Datum.b.collate('RTRIM')), [1, 2, 3, 4])\n\n        # Text comparison d=a is performed using the NOCASE collating sequence.\n        assertC(base.where(Datum.d == Datum.a), [1, 2, 3, 4])\n\n        # Text comparison a=d is performed using the BINARY collating sequence.\n        assertC(base.where(Datum.a == Datum.d), [1, 4])\n\n        # Text comparison 'abc'=c is performed using RTRIM collating sequence.\n        assertC(base.where('abc' == Datum.c), [1, 2, 3])\n\n        # Text comparison c='abc' is performed using RTRIM collating sequence.\n        assertC(base.where(Datum.c == 'abc'), [1, 2, 3])\n\n        # Grouping is performed using the NOCASE collating sequence (Values\n        # 'abc', 'ABC', and 'Abc' are placed in the same group).\n        query = Datum.select(fn.COUNT(Datum.id)).group_by(Datum.d)\n        self.assertEqual(query.scalar(), 4)\n\n        # Grouping is performed using the BINARY collating sequence.  'abc' and\n        # 'ABC' and 'Abc' form different groups.\n        query = Datum.select(fn.COUNT(Datum.id)).group_by(Datum.d.concat(''))\n        self.assertEqual([r[0] for r in query.tuples()], [1, 1, 2])\n\n        # Sorting or column c is performed using the RTRIM collating sequence.\n        assertC(base.order_by(Datum.c, Datum.id), [4, 1, 2, 3])\n\n        # Sorting of (c||'') is performed using the BINARY collating sequence.\n        assertC(base.order_by(Datum.c.concat(''), Datum.id), [4, 2, 3, 1])\n\n        # Sorting of column c is performed using the NOCASE collating sequence.\n        assertC(base.order_by(Datum.c.collate('NOCASE'), Datum.id),\n                [2, 4, 3, 1])\n\n\nclass TestReadOnly(ModelTestCase):\n    database = get_sqlite_db()\n\n    @requires_models(User)\n    def test_read_only(self):\n        User.create(username='foo')\n\n        db_filename = self.database.database\n        db = SqliteDatabase('file:%s?mode=ro' % db_filename, uri=True)\n        cursor = db.execute_sql('select username from users')\n        self.assertEqual(cursor.fetchone(), ('foo',))\n\n        self.assertRaises(OperationalError, db.execute_sql,\n                          'insert into users (username) values (?)', ('huey',))\n\n        # We cannot create a database if in read-only mode.\n        db = SqliteDatabase('file:xx_not_exists.db?mode=ro', uri=True)\n        self.assertRaises(OperationalError, db.connect)\n\n\nclass TDecModel(TestModel):\n    value = TDecimalField(max_digits=24, decimal_places=16, auto_round=True)\n\n\nclass TestTDecimalField(ModelTestCase):\n    database = database\n    requires = [TDecModel]\n\n    def test_tdecimal_field(self):\n        value = D('12345678.0123456789012345')\n        value_ov = D('12345678.012345678901234567890123456789')\n\n        td1 = TDecModel.create(value=value)\n        td2 = TDecModel.create(value=value_ov)\n\n        td1_db = TDecModel.get(TDecModel.id == td1.id)\n        self.assertEqual(td1_db.value, value)\n\n        td2_db = TDecModel.get(TDecModel.id == td2.id)\n        self.assertEqual(td2_db.value, D('12345678.0123456789012346'))\n\n\nclass KVR(TestModel):\n    key = TextField(primary_key=True)\n    value = IntegerField()\n\n\n@skip_unless(database.server_version >= (3, 35, 0), 'sqlite returning clause required')\nclass TestSqliteReturning(ModelTestCase):\n    database = database\n    requires = [Person, User, KVR]\n\n    def test_sqlite_returning(self):\n        iq = (User\n              .insert_many([{'username': 'u%s' % i} for i in range(3)])\n              .returning(User.id))\n        self.assertEqual([r.id for r in iq.execute()], [1, 2, 3])\n\n        res = (User\n               .insert_many([{'username': 'u%s' % i} for i in (4, 5)])\n               .returning(User)\n               .execute())\n        self.assertEqual([(r.id, r.username) for r in res],\n                         [(4, 'u4'), (5, 'u5')])\n\n        # Simple insert returns the ID.\n        res = User.insert(username='u6').execute()\n        self.assertEqual(res, 6)\n\n        iq = (User\n              .insert_many([{'username': 'u%s' % i} for i in (7, 8, 9)])\n              .returning(User)\n              .namedtuples())\n        curs = iq.execute()\n        self.assertEqual([u.id for u in curs], [7, 8, 9])\n\n    def test_sqlite_on_conflict_returning(self):\n        p = Person.create(first='f1', last='l1', dob='1990-01-01')\n        self.assertEqual(p.id, 1)\n\n        iq = Person.insert_many([\n            {'first': 'f%s' % i, 'last': 'l%s' %i, 'dob': '1990-01-%02d' % i}\n            for i in range(1, 3)])\n        iq = iq.on_conflict(conflict_target=[Person.first, Person.last],\n                            update={'dob': '2000-01-01'})\n        p1, p2 = iq.returning(Person).execute()\n\n        self.assertEqual((p1.first, p1.last), ('f1', 'l1'))\n        self.assertEqual(p1.dob, datetime.date(2000, 1, 1))\n        self.assertEqual((p2.first, p2.last), ('f2', 'l2'))\n        self.assertEqual(p2.dob, datetime.date(1990, 1, 2))\n\n        p3 = Person.insert(first='f3', last='l3', dob='1990-01-03').execute()\n        self.assertEqual(p3, 3)\n\n    def test_text_pk(self):\n        res = KVR.create(key='k1', value=1)\n        self.assertEqual((res.key, res.value), ('k1', 1))\n\n        res = KVR.insert(key='k2', value=2).execute()\n        self.assertEqual(res, 2)\n        #self.assertEqual(res, 'k2')\n\n        # insert_many() returns the primary-key as usual.\n        iq = (KVR\n              .insert_many([{'key': 'k%s' % i, 'value': i} for i in (3, 4)])\n              .returning(KVR.key))\n        self.assertEqual([r.key for r in iq.execute()], ['k3', 'k4'])\n\n        iq = KVR.insert_many([{'key': 'k%s' % i, 'value': i} for i in (4, 5)])\n        iq = iq.on_conflict(conflict_target=[KVR.key],\n                            update={KVR.value: KVR.value + 10})\n        res = iq.returning(KVR).execute()\n        self.assertEqual([(r.key, r.value) for r in res],\n                         [('k4', 14), ('k5', 5)])\n\n        res = (KVR\n               .update(value=KVR.value + 10)\n               .where(KVR.key.in_(['k1', 'k3', 'kx']))\n               .returning(KVR)\n               .execute())\n        self.assertEqual([(r.key, r.value) for r in res],\n                         [('k1', 11), ('k3', 13)])\n\n        res = (KVR.delete()\n               .where(KVR.key.not_in(['k2', 'k3', 'k4']))\n               .returning(KVR)\n               .execute())\n        self.assertEqual([(r.key, r.value) for r in res],\n                         [('k1', 11), ('k5', 5)])\n\n\n@skip_unless(database.server_version >= (3, 35, 0), 'sqlite returning clause required')\nclass TestSqliteReturningConfig(ModelTestCase):\n    database = SqliteDatabase(':memory:', returning_clause=True)\n    requires = [KVR, User]\n\n    def test_pk_set_properly(self):\n        user = User.create(username='u1')\n        self.assertEqual(user.id, 1)\n\n        kvr = KVR.create(key='k1', value=1)\n        self.assertEqual(kvr.key, 'k1')\n\n    def test_insert_behavior(self):\n        iq = User.insert({'username': 'u1'})\n        self.assertEqual(iq.execute(), 1)\n\n        iq = User.insert_many([{'username': 'u2'}, {'username': 'u3'}])\n        self.assertEqual(list(iq.execute()), [(2,), (3,)])\n\n        # NOTE: sqlite3_changes() does not return the inserted rowcount until\n        # the statement has been consumed. The fact that it returned 2 is a\n        # side-effect of the statement cache and our having consumed the query\n        # in the previous test assertion. So this test is invalid.\n        #iq = User.insert_many([('u4',), ('u5',)]).as_rowcount()\n        #self.assertEqual(iq.execute(), 2)\n\n        iq = KVR.insert({'key': 'k1', 'value': 1})\n        self.assertEqual(iq.execute(), 'k1')\n\n        iq = KVR.insert_many([('k2', 2), ('k3', 3)])\n        self.assertEqual(list(iq.execute()), [('k2',), ('k3',)])\n\n        # See note above.\n        #iq = KVR.insert_many([('k4', 4), ('k5', 5)]).as_rowcount()\n        #self.assertEqual(iq.execute(), 2)\n\n    def test_insert_on_conflict(self):\n        KVR.create(key='k1', value=1)\n        iq = (KVR.insert({'key': 'k1', 'value': 100})\n              .on_conflict(conflict_target=[KVR.key],\n                           update={KVR.value: KVR.value + 10}))\n        self.assertEqual(iq.execute(), 'k1')\n        self.assertEqual(KVR.get(KVR.key == 'k1').value, 11)\n\n        KVR.create(key='k2', value=2)\n        iq = (KVR.insert_many([\n            {'key': 'k1', 'value': 100},\n            {'key': 'k2', 'value': 200},\n            {'key': 'k3', 'value': 300}])\n            .on_conflict(conflict_target=[KVR.key],\n                         update={KVR.value: KVR.value + 10}))\n        self.assertEqual(list(iq.execute()), [('k1',), ('k2',), ('k3',)])\n        self.assertEqual(sorted(KVR.select().tuples()),\n                         [('k1', 21), ('k2', 12), ('k3', 300)])\n\n    def test_update_delete_rowcounts(self):\n        users = [User.create(username=u) for u in 'abc']\n        kvrs = [KVR.create(key='k%s' % i, value=i) for i in (1, 2, 3)]\n\n        uq = User.update(username='c2').where(User.username == 'c')\n        self.assertEqual(uq.execute(), 1)\n        uq = User.update(username=User.username.concat('x'))\n        self.assertEqual(uq.execute(), 3)\n\n        dq = User.delete().where(User.username.in_(['bx', 'c2x']))\n        self.assertEqual(dq.execute(), 2)\n\n        uq = KVR.update(value=KVR.value + 10).where(KVR.key == 'k3')\n        self.assertEqual(uq.execute(), 1)\n        uq = KVR.update(value=KVR.value + 100)\n        self.assertEqual(uq.execute(), 3)\n\n        dq = KVR.delete().where(KVR.value.in_([102, 113]))\n        self.assertEqual(dq.execute(), 2)\n\n    def test_update_delete_explicit_returning(self):\n        users = [User.create(username=u) for u in 'abc']\n\n        uq = (User.update(username='c2')\n              .where(User.username == 'c')\n              .returning(User.id, User.username))\n        for _ in range(2):\n            self.assertEqual([u.username for u in uq.execute()], ['c2'])\n        self.assertEqual(list(uq.clone().execute()), [])\n\n        uq = (User.update(username=User.username.concat('x'))\n              .where(~User.username.endswith('x'))  # For idempotency.\n              .returning(User.id, User.username)\n              .tuples())\n        for _ in range(2):\n            self.assertEqual(sorted(uq.execute()),\n                             [(1, 'ax'), (2, 'bx'), (3, 'c2x')])\n        self.assertEqual(list(uq.clone().execute()), [])\n\n        dq = User.delete().where(User.username == 'c2x').returning(User)\n        for _ in range(2):\n            # The result is cached to support multiple iterations.\n            self.assertEqual([u.username for u in dq.execute()], ['c2x'])\n        self.assertEqual(list(dq.clone().execute()), [])\n\n        dq = User.delete().returning(User).tuples()\n        for _ in range(2):\n            # The result is cached to support multiple iterations.\n            self.assertEqual(sorted(dq.execute()), [(1, 'ax'), (2, 'bx')])\n        self.assertEqual(list(dq.clone().execute()), [])\n\n    def test_bulk_create_update(self):\n        users = [User(username='u%s' % i) for i in range(5)]\n        with self.assertQueryCount(1):\n            User.bulk_create(users)\n\n        self.assertEqual(User.select().count(), 5)\n        self.assertEqual(sorted(User.select().tuples()), [\n            (1, 'u0'), (2, 'u1'), (3, 'u2'), (4, 'u3'), (5, 'u4')])\n\n        users[0].username = 'u0x'\n        users[2].username = 'u2x'\n        users[4].username = 'u4x'\n        with self.assertQueryCount(1):\n            n = User.bulk_update(users, ['username'])\n            self.assertEqual(n, 5)\n\n        self.assertEqual(sorted(User.select().tuples()), [\n            (1, 'u0x'), (2, 'u1'), (3, 'u2x'), (4, 'u3'), (5, 'u4x')])\n\n    @requires_models(User, Tweet)\n    def test_fk_set_correctly(self):\n        # Ensure FK can be set lazily.\n        user = User(username='u1')\n        tweet = Tweet(user=user, content='t1')\n        user.save()\n        tweet.save()\n\n\n@skip_unless(database.server_version >= (3, 20, 0), 'sqlite deterministic requires >= 3.20')\n@skip_unless(sys.version_info >= (3, 8, 0), 'sqlite deterministic requires Python >= 3.8')\nclass TestDeterministicFunction(ModelTestCase):\n    database = get_in_memory_db()\n\n    def test_deterministic(self):\n        db = self.database\n        @db.func(deterministic=True)\n        def pylower(s):\n            if s is not None:\n                return s.lower()\n\n        class Reg(db.Model):\n            key = TextField()\n            class Meta:\n                indexes = [\n                    SQL('create unique index \"reg_pylower_key\" '\n                        'on \"reg\" (pylower(\"key\"))')]\n\n        db.create_tables([Reg])\n        Reg.create(key='k1')\n        with self.assertRaises(IntegrityError):\n            with db.atomic():\n                Reg.create(key='K1')\n\n@skip_unless(sys.version_info >= (3, 7, 0), 'isoformat (\":\") works 3.7+')\nclass TestISODateTimeField(ModelTestCase):\n    database = get_in_memory_db()\n    requires = [DT]\n\n    def test_aware_datetimes(self):\n        class _UTC(datetime.tzinfo):\n            def utcoffset(self, dt): return datetime.timedelta(0)\n            def tzname(self, dt): return \"UTC\"\n            def dst(self, dt): return datetime.timedelta(0)\n\n        UTC = _UTC()\n\n        d1 = datetime.datetime(2026, 1, 2, 3, 4, 5)\n        d2 = d1.astimezone(UTC)\n\n        dt = DT.create(key='k1', d=d1, iso=d2)\n        self.assertEqual(dt.d, d1)\n        self.assertEqual(dt.iso, d2)\n\n        dt = DT['k1']\n        self.assertEqual(dt.d, d1)\n        self.assertEqual(dt.iso, d2)\n\n        raw = self.database.execute_sql('select * from dt').fetchone()\n        self.assertEqual(raw, ('k1', str(d1), d2.isoformat()))\n\n#\n# If we have cysqlite, let's run tests on it.\n#\n\ntry:\n    from playhouse.cysqlite_ext import CySqliteDatabase\nexcept ImportError:\n    pass\nelse:\n    cysqlite_database = CySqliteDatabase('peewee_test.db', timeout=100)\n    cysqlite_database.register_aggregate(WeightedAverage, 'weighted_avg', 1)\n    cysqlite_database.register_aggregate(WeightedAverage, 'weighted_avg2', 2)\n    cysqlite_database.register_collation(collate_reverse)\n    cysqlite_database.register_function(title_case)\n    cysqlite_database.collation()(collate_case_insensitive)\n    cysqlite_database.func()(rstrip)\n\n    test_cases = [\n        TestJSONField,\n        TestJSONFieldFunctions,\n        TestJSONBFieldFunctions,\n        TestSqliteExtensions,\n        TestFullTextSearch,\n        TestFTS5,\n        TestUserDefinedCallbacks,\n        TestRowIDField,\n        TestIntWhereChain,\n        TestCollatedFieldDefinitions,\n        TestReadOnly,\n        TestSqliteReturning,\n        TestDeterministicFunction,\n        TestISODateTimeField,\n        # For various reasons these do not work.\n        #TestJsonContains,\n        #TestTDecimalField,\n        #TestSqliteReturningConfig,\n    ]\n\n    for test_case in test_cases:\n        new_name = test_case.__name__ + 'CySqlite'\n        klass = type(new_name, (test_case,), {\n            'database': cysqlite_database,\n        })\n        locals()[new_name] = klass\n"
  },
  {
    "path": "tests/sqlite_changelog.py",
    "content": "import datetime\n\nfrom peewee import *\nfrom playhouse.sqlite_changelog import ChangeLog\nfrom playhouse.sqlite_ext import JSONField\n\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import requires_models\nfrom .base import skip_unless\nfrom .sqlite_helpers import json_installed\n\n\ndatabase = SqliteDatabase(':memory:', pragmas={'foreign_keys': 1})\n\n\nclass Person(TestModel):\n    name = TextField()\n    dob = DateField()\n\n\nclass Note(TestModel):\n    person = ForeignKeyField(Person, on_delete='CASCADE')\n    content = TextField()\n    timestamp = TimestampField()\n    status = IntegerField(default=0)\n\n\nclass CT1(TestModel):\n    f1 = TextField()\n    f2 = IntegerField(null=True)\n    f3 = FloatField()\n    fi = IntegerField()\n\n\nclass CT2(TestModel):\n    data = JSONField()  # Diff of json?\n\n\nchangelog = ChangeLog(database)\nCL = changelog.model\n\n\n@skip_unless(json_installed(), 'requires sqlite json1')\nclass TestChangeLog(ModelTestCase):\n    database = database\n    requires = [Person, Note]\n\n    def setUp(self):\n        super(TestChangeLog, self).setUp()\n        changelog.install(Person)\n        changelog.install(Note, skip_fields=['timestamp'])\n        self.last_index = 0\n\n    def assertChanges(self, changes, last_index=None):\n        last_index = last_index or self.last_index\n        query = (CL\n                 .select(CL.action, CL.table, CL.changes)\n                 .order_by(CL.id)\n                 .offset(last_index))\n        accum = list(query.tuples())\n        self.last_index += len(accum)\n        self.assertEqual(accum, changes)\n\n    def test_changelog(self):\n        huey = Person.create(name='huey', dob=datetime.date(2010, 5, 1))\n        zaizee = Person.create(name='zaizee', dob=datetime.date(2013, 1, 1))\n        self.assertChanges([\n            ('INSERT', 'person', {'name': [None, 'huey'],\n                                  'dob': [None, '2010-05-01']}),\n            ('INSERT', 'person', {'name': [None, 'zaizee'],\n                                  'dob': [None, '2013-01-01']})])\n\n        zaizee.dob = datetime.date(2013, 2, 2)\n        zaizee.save()\n        self.assertChanges([\n            ('UPDATE', 'person', {'dob': ['2013-01-01', '2013-02-02']})])\n\n        zaizee.name = 'zaizee-x'\n        zaizee.dob = datetime.date(2013, 3, 3)\n        zaizee.save()\n\n        huey.save()  # No changes.\n\n        self.assertChanges([\n            ('UPDATE', 'person', {'name': ['zaizee', 'zaizee-x'],\n                                  'dob': ['2013-02-02', '2013-03-03']}),\n            ('UPDATE', 'person', {})])\n\n        zaizee.delete_instance()\n        self.assertChanges([\n            ('DELETE', 'person', {'name': ['zaizee-x', None],\n                                  'dob': ['2013-03-03', None]})])\n\n        nh1 = Note.create(person=huey, content='huey1', status=1)\n        nh2 = Note.create(person=huey, content='huey2', status=2)\n        self.assertChanges([\n            ('INSERT', 'note', {'person_id': [None, huey.id],\n                                'content': [None, 'huey1'],\n                                'status': [None, 1]}),\n            ('INSERT', 'note', {'person_id': [None, huey.id],\n                                'content': [None, 'huey2'],\n                                'status': [None, 2]})])\n\n        nh1.content = 'huey1-x'\n        nh1.status = 0\n        nh1.save()\n\n        mickey = Person.create(name='mickey', dob=datetime.date(2009, 8, 1))\n        nh2.person = mickey\n        nh2.save()\n\n        self.assertChanges([\n            ('UPDATE', 'note', {'content': ['huey1', 'huey1-x'],\n                                'status': [1, 0]}),\n            ('INSERT', 'person', {'name': [None, 'mickey'],\n                                  'dob': [None, '2009-08-01']}),\n            ('UPDATE', 'note', {'person_id': [huey.id, mickey.id]})])\n\n        mickey.delete_instance()\n        self.assertChanges([\n            ('DELETE', 'note', {'person_id': [mickey.id, None],\n                                'content': ['huey2', None],\n                                'status': [2, None]}),\n            ('DELETE', 'person', {'name': ['mickey', None],\n                                  'dob': ['2009-08-01', None]})])\n\n    @requires_models(CT1)\n    def test_changelog_details(self):\n        changelog.install(CT1, skip_fields=['fi'], insert=False, delete=False)\n\n        c1 = CT1.create(f1='v1', f2=1, f3=1.5, fi=0)\n        self.assertChanges([])\n\n        CT1.update(f1='v1-x', f2=2, f3=2.5, fi=1).execute()\n        self.assertChanges([\n            ('UPDATE', 'ct1', {\n                'f1': ['v1', 'v1-x'],\n                'f2': [1, 2],\n                'f3': [1.5, 2.5]})])\n\n        c1.f2 = None\n        c1.save()  # Overwrites previously-changed fields.\n        self.assertChanges([('UPDATE', 'ct1', {\n            'f1': ['v1-x', 'v1'],\n            'f2': [2, None],\n            'f3': [2.5, 1.5]})])\n\n        c1.delete_instance()\n        self.assertChanges([])\n\n    @requires_models(CT2)\n    def test_changelog_jsonfield(self):\n        changelog.install(CT2)\n\n        ca = CT2.create(data={'k1': 'v1'})\n        cb = CT2.create(data=['i0', 'i1', 'i2'])\n        cc = CT2.create(data='hello')\n\n        self.assertChanges([\n            ('INSERT', 'ct2', {'data': [None, {'k1': 'v1'}]}),\n            ('INSERT', 'ct2', {'data': [None, ['i0', 'i1', 'i2']]}),\n            ('INSERT', 'ct2', {'data': [None, 'hello']})])\n\n        ca.data['k1'] = 'v1-x'\n        cb.data.append('i3')\n        cc.data = 'world'\n\n        ca.save()\n        cb.save()\n        cc.save()\n\n        self.assertChanges([\n            ('UPDATE', 'ct2', {'data': [{'k1': 'v1'}, {'k1': 'v1-x'}]}),\n            ('UPDATE', 'ct2', {'data': [['i0', 'i1', 'i2'],\n                                        ['i0', 'i1', 'i2', 'i3']]}),\n            ('UPDATE', 'ct2', {'data': ['hello', 'world']})])\n\n        cc.data = 13.37\n        cc.save()\n        self.assertChanges([('UPDATE', 'ct2', {'data': ['world', 13.37]})])\n\n        ca.delete_instance()\n        self.assertChanges([\n            ('DELETE', 'ct2', {'data': [{'k1': 'v1-x'}, None]})])\n"
  },
  {
    "path": "tests/sqlite_helpers.py",
    "content": "from peewee import sqlite3\n\n\ndef json_installed():\n    if sqlite3.sqlite_version_info < (3, 9, 0):\n        return False\n    tmp_db = sqlite3.connect(':memory:')\n    try:\n        tmp_db.execute('select json(?)', (1337,))\n    except:\n        return False\n    finally:\n        tmp_db.close()\n    return True\n\n\ndef json_patch_installed():\n    return sqlite3.sqlite_version_info >= (3, 18, 0)\n\n\ndef json_text_installed():\n    return sqlite3.sqlite_version_info >= (3, 38, 0)\n\ndef jsonb_installed():\n    return sqlite3.sqlite_version_info >= (3, 45, 0)\n\n\ndef compile_option(p):\n    if not hasattr(compile_option, '_pragma_cache'):\n        conn = sqlite3.connect(':memory:')\n        curs = conn.execute('pragma compile_options')\n        opts = [opt.lower().split('=')[0].strip() for opt, in curs.fetchall()]\n        compile_option._pragma_cache = set(opts)\n    return p in compile_option._pragma_cache\n"
  },
  {
    "path": "tests/sqlite_udf.py",
    "content": "import datetime\nimport json\nimport random\n\nfrom peewee import *\nfrom peewee import sqlite3\nfrom playhouse.sqlite_udf import register_all\n\nfrom .base import IS_SQLITE_9\nfrom .base import ModelTestCase\nfrom .base import TestModel\nfrom .base import get_sqlite_db\nfrom .base import skip_unless\ntry:\n    from playhouse import _sqlite_udf as cython_udf\nexcept ImportError:\n    cython_udf = None\n\n\ndef requires_cython(method):\n    return skip_unless(cython_udf is not None,\n                       'requires sqlite udf c extension')(method)\n\ndatabase = get_sqlite_db()\nregister_all(database)\n\n\nclass User(TestModel):\n    username = TextField()\n\n\nclass APIResponse(TestModel):\n    url = TextField(default='')\n    data = TextField(default='')\n    timestamp = DateTimeField(default=datetime.datetime.now)\n\n\nclass Generic(TestModel):\n    value = IntegerField(default=0)\n    x = Field(null=True)\n\n\nMODELS = [User, APIResponse, Generic]\n\n\nclass FixedOffset(datetime.tzinfo):\n    def __init__(self, offset, name, dstoffset=42):\n        if isinstance(offset, int):\n            offset = datetime.timedelta(minutes=offset)\n        if isinstance(dstoffset, int):\n            dstoffset = datetime.timedelta(minutes=dstoffset)\n        self.__offset = offset\n        self.__name = name\n        self.__dstoffset = dstoffset\n\n    def utcoffset(self, dt):\n        return self.__offset\n\n    def tzname(self, dt):\n        return self.__name\n\n    def dst(self, dt):\n        return self.__dstoffset\n\n\nclass BaseTestUDF(ModelTestCase):\n    database = database\n\n    def sql1(self, sql, *params):\n        cursor = self.database.execute_sql(sql, params)\n        return cursor.fetchone()[0]\n\n\nclass TestAggregates(BaseTestUDF):\n    requires = [Generic]\n\n    def _store_values(self, *values):\n        with self.database.atomic():\n            for value in values:\n                Generic.create(x=value)\n\n    def mts(self, seconds):\n        return (datetime.datetime(2015, 1, 1) +\n                datetime.timedelta(seconds=seconds))\n\n    def test_min_avg_tdiff(self):\n        self.assertEqual(self.sql1('select mintdiff(x) from generic;'), None)\n        self.assertEqual(self.sql1('select avgtdiff(x) from generic;'), None)\n\n        self._store_values(self.mts(10))\n        self.assertEqual(self.sql1('select mintdiff(x) from generic;'), None)\n        self.assertEqual(self.sql1('select avgtdiff(x) from generic;'), 0)\n\n        self._store_values(self.mts(15))\n        self.assertEqual(self.sql1('select mintdiff(x) from generic;'), 5)\n        self.assertEqual(self.sql1('select avgtdiff(x) from generic;'), 5)\n\n        self._store_values(\n            self.mts(22),\n            self.mts(52),\n            self.mts(18),\n            self.mts(41),\n            self.mts(2),\n            self.mts(33))\n        self.assertEqual(self.sql1('select mintdiff(x) from generic;'), 3)\n        self.assertEqual(\n            round(self.sql1('select avgtdiff(x) from generic;'), 1),\n            7.1)\n\n        self._store_values(self.mts(22))\n        self.assertEqual(self.sql1('select mintdiff(x) from generic;'), 0)\n\n    def test_duration(self):\n        self.assertEqual(self.sql1('select duration(x) from generic;'), None)\n\n        self._store_values(self.mts(10))\n        self.assertEqual(self.sql1('select duration(x) from generic;'), 0)\n\n        self._store_values(self.mts(15))\n        self.assertEqual(self.sql1('select duration(x) from generic;'), 5)\n\n        self._store_values(\n            self.mts(22),\n            self.mts(11),\n            self.mts(52),\n            self.mts(18),\n            self.mts(41),\n            self.mts(2),\n            self.mts(33))\n        self.assertEqual(self.sql1('select duration(x) from generic;'), 50)\n\n    @requires_cython\n    def test_median(self):\n        self.assertEqual(self.sql1('select median(x) from generic;'), None)\n\n        self._store_values(1)\n        self.assertEqual(self.sql1('select median(x) from generic;'), 1)\n\n        self._store_values(3, 6, 6, 6, 7, 7, 7, 7, 12, 12, 17)\n        self.assertEqual(self.sql1('select median(x) from generic;'), 7)\n\n        Generic.delete().execute()\n        self._store_values(9, 2, 2, 3, 3, 1)\n        self.assertEqual(self.sql1('select median(x) from generic;'), 3)\n\n        Generic.delete().execute()\n        self._store_values(4, 4, 1, 8, 2, 2, 5, 8, 1)\n        self.assertEqual(self.sql1('select median(x) from generic;'), 4)\n\n    def test_mode(self):\n        self.assertEqual(self.sql1('select mode(x) from generic;'), None)\n\n        self._store_values(1)\n        self.assertEqual(self.sql1('select mode(x) from generic;'), 1)\n\n        self._store_values(4, 5, 6, 1, 3, 4, 1, 4, 9, 3, 4)\n        self.assertEqual(self.sql1('select mode(x) from generic;'), 4)\n\n    def test_ranges(self):\n        self.assertEqual(self.sql1('select minrange(x) from generic'), None)\n        self.assertEqual(self.sql1('select avgrange(x) from generic'), None)\n        self.assertEqual(self.sql1('select range(x) from generic'), None)\n\n        self._store_values(1)\n        self.assertEqual(self.sql1('select minrange(x) from generic'), 0)\n        self.assertEqual(self.sql1('select avgrange(x) from generic'), 0)\n        self.assertEqual(self.sql1('select range(x) from generic'), 0)\n\n        self._store_values(4, 8, 13, 19)\n        self.assertEqual(self.sql1('select minrange(x) from generic'), 3)\n        self.assertEqual(self.sql1('select avgrange(x) from generic'), 4.5)\n        self.assertEqual(self.sql1('select range(x) from generic'), 18)\n\n        Generic.delete().execute()\n        self._store_values(19, 4, 5, 20, 5, 8)\n        self.assertEqual(self.sql1('select range(x) from generic'), 16)\n\n\nclass TestScalarFunctions(BaseTestUDF):\n    requires = MODELS\n\n    def test_if_then_else(self):\n        for i in range(4):\n            User.create(username='u%d' % (i + 1))\n        with self.assertQueryCount(1):\n            query = (User\n                     .select(\n                         User.username,\n                         fn.if_then_else(\n                             User.username << ['u1', 'u2'],\n                             'one or two',\n                             'other').alias('name_type'))\n                     .order_by(User.id))\n            self.assertEqual([row.name_type for row in query], [\n                'one or two',\n                'one or two',\n                'other',\n                'other'])\n\n    def test_strip_tz(self):\n        dt = datetime.datetime(2015, 1, 1, 12, 0)\n        # 13 hours, 37 minutes.\n        dt_tz = dt.replace(tzinfo=FixedOffset(13 * 60 + 37, 'US/LFK'))\n        api_dt = APIResponse.create(timestamp=dt)\n        api_dt_tz = APIResponse.create(timestamp=dt_tz)\n\n        # Re-fetch from the database.\n        api_dt_db = APIResponse.get(APIResponse.id == api_dt.id)\n        api_dt_tz_db = APIResponse.get(APIResponse.id == api_dt_tz.id)\n\n        # Assert the timezone is present, first of all, and that they were\n        # stored in the database.\n        self.assertEqual(api_dt_db.timestamp, dt)\n\n        query = (APIResponse\n                 .select(\n                     APIResponse.id,\n                     fn.strip_tz(APIResponse.timestamp).alias('ts'))\n                 .order_by(APIResponse.id))\n        ts, ts_tz = query[:]\n\n        self.assertEqual(ts.ts, dt)\n        self.assertEqual(ts_tz.ts, dt)\n\n    def test_human_delta(self):\n        values = [0, 1, 30, 300, 3600, 7530, 300000]\n        for value in values:\n            Generic.create(value=value)\n\n        delta = fn.human_delta(Generic.value).coerce(False)\n        query = (Generic\n                 .select(\n                     Generic.value,\n                     delta.alias('delta'))\n                 .order_by(Generic.value))\n        results = query.tuples()[:]\n        self.assertEqual(results, [\n            (0, '0 seconds'),\n            (1, '1 second'),\n            (30, '30 seconds'),\n            (300, '5 minutes'),\n            (3600, '1 hour'),\n            (7530, '2 hours, 5 minutes, 30 seconds'),\n            (300000, '3 days, 11 hours, 20 minutes'),\n        ])\n\n    def test_file_ext(self):\n        data = (\n            ('test.py', '.py'),\n            ('test.x.py', '.py'),\n            ('test', ''),\n            ('test.', '.'),\n            ('/foo.bar/test/nug.py', '.py'),\n            ('/foo.bar/test/nug', ''),\n        )\n        for filename, ext in data:\n            res = self.sql1('SELECT file_ext(?)', filename)\n            self.assertEqual(res, ext)\n\n    def test_gz(self):\n        random.seed(1)\n        A = ord('A')\n        z = ord('z')\n        with self.database.atomic():\n            def randstr(l):\n                return ''.join([\n                    chr(random.randint(A, z))\n                    for _ in range(l)])\n\n            data = (\n                'a',\n                'a' * 1024,\n                randstr(1024),\n                randstr(4096),\n                randstr(1024 * 64))\n            for s in data:\n                compressed = self.sql1('select gzip(?)', s)\n                decompressed = self.sql1('select gunzip(?)', compressed)\n                self.assertEqual(decompressed.decode('utf-8'), s)\n\n    def test_hostname(self):\n        r = json.dumps({'success': True})\n        data = (\n            ('https://charlesleifer.com/api/', r),\n            ('https://a.charlesleifer.com/api/foo', r),\n            ('www.nugget.com', r),\n            ('nugz.com', r),\n            ('http://a.b.c.peewee/foo', r),\n            ('https://charlesleifer.com/xx', r),\n            ('https://charlesleifer.com/xx', r),\n        )\n        with self.database.atomic():\n            for url, response in data:\n                APIResponse.create(url=url, data=data)\n\n        with self.assertQueryCount(1):\n            query = (APIResponse\n                     .select(\n                         fn.hostname(APIResponse.url).alias('host'),\n                         fn.COUNT(APIResponse.id).alias('count'))\n                     .group_by(fn.hostname(APIResponse.url))\n                     .order_by(\n                         fn.COUNT(APIResponse.id).desc(),\n                         fn.hostname(APIResponse.url)))\n            results = query.tuples()[:]\n\n        self.assertEqual(results, [\n            ('charlesleifer.com', 3),\n            ('', 2),\n            ('a.b.c.peewee', 1),\n            ('a.charlesleifer.com', 1)])\n\n    @skip_unless(IS_SQLITE_9, 'requires sqlite >= 3.9')\n    def test_toggle(self):\n        self.assertEqual(self.sql1('select toggle(?)', 'foo'), 1)\n        self.assertEqual(self.sql1('select toggle(?)', 'bar'), 1)\n        self.assertEqual(self.sql1('select toggle(?)', 'foo'), 0)\n        self.assertEqual(self.sql1('select toggle(?)', 'foo'), 1)\n        self.assertEqual(self.sql1('select toggle(?)', 'bar'), 0)\n\n        self.assertEqual(self.sql1('select clear_toggles()'), None)\n        self.assertEqual(self.sql1('select toggle(?)', 'foo'), 1)\n\n    def test_setting(self):\n        self.assertEqual(self.sql1('select setting(?, ?)', 'k1', 'v1'), 'v1')\n        self.assertEqual(self.sql1('select setting(?, ?)', 'k2', 'v2'), 'v2')\n\n        self.assertEqual(self.sql1('select setting(?)', 'k1'), 'v1')\n\n        self.assertEqual(self.sql1('select setting(?, ?)', 'k2', 'v2-x'), 'v2-x')\n        self.assertEqual(self.sql1('select setting(?)', 'k2'), 'v2-x')\n\n        self.assertEqual(self.sql1('select setting(?)', 'kx'), None)\n\n        self.assertEqual(self.sql1('select clear_settings()'), None)\n        self.assertEqual(self.sql1('select setting(?)', 'k1'), None)\n\n    def test_random_range(self):\n        vals = ((1, 10), (1, 100), (0, 2), (1, 5, 2))\n        results = []\n        for params in vals:\n            random.seed(1)\n            results.append(random.randrange(*params))\n\n        for params, expected in zip(vals, results):\n            random.seed(1)\n            if len(params) == 3:\n                pstr = '?, ?, ?'\n            else:\n                pstr = '?, ?'\n            self.assertEqual(\n                self.sql1('select randomrange(%s)' % pstr, *params),\n                expected)\n\n    def test_sqrt(self):\n        self.assertEqual(self.sql1('select sqrt(?)', 4), 2)\n        self.assertEqual(round(self.sql1('select sqrt(?)', 2), 2), 1.41)\n\n    def test_tonumber(self):\n        data = (\n            ('123', 123),\n            ('1.23', 1.23),\n            ('1e4', 10000),\n            ('-10', -10),\n            ('x', None),\n            ('13d', None),\n        )\n        for inp, outp in data:\n            self.assertEqual(self.sql1('select tonumber(?)', inp), outp)\n\n    @requires_cython\n    def test_leven(self):\n        self.assertEqual(\n            self.sql1('select levenshtein_dist(?, ?)', 'abc', 'ba'),\n            2)\n\n        self.assertEqual(\n            self.sql1('select levenshtein_dist(?, ?)', 'abcde', 'eba'),\n            4)\n\n        self.assertEqual(\n            self.sql1('select levenshtein_dist(?, ?)', 'abcde', 'abcde'),\n            0)\n\n    @requires_cython\n    def test_str_dist(self):\n        self.assertEqual(\n            self.sql1('select str_dist(?, ?)', 'abc', 'ba'),\n            3)\n\n        self.assertEqual(\n            self.sql1('select str_dist(?, ?)', 'abcde', 'eba'),\n            6)\n\n        self.assertEqual(\n            self.sql1('select str_dist(?, ?)', 'abcde', 'abcde'),\n            0)\n\n    def test_substr_count(self):\n        self.assertEqual(\n            self.sql1('select substr_count(?, ?)', 'foo bar baz', 'a'), 2)\n        self.assertEqual(\n            self.sql1('select substr_count(?, ?)', 'foo bor baz', 'o'), 3)\n        self.assertEqual(\n            self.sql1('select substr_count(?, ?)', 'foodooboope', 'oo'), 3)\n        self.assertEqual(self.sql1('select substr_count(?, ?)', 'xx', ''), 0)\n        self.assertEqual(self.sql1('select substr_count(?, ?)', '', ''), 0)\n\n    def test_strip_chars(self):\n        self.assertEqual(\n            self.sql1('select strip_chars(?, ?)', '  hey foo ', ' '),\n            'hey foo')\n"
  },
  {
    "path": "tests/sqliteq.py",
    "content": "import os\nimport sys\nimport threading\nimport time\nimport unittest\nfrom functools import partial\n\ntry:\n    import gevent\n    from gevent.event import Event as GreenEvent\nexcept ImportError:\n    gevent = None\n\nfrom peewee import *\nfrom playhouse.sqliteq import ResultTimeout\nfrom playhouse.sqliteq import SqliteQueueDatabase\nfrom playhouse.sqliteq import WriterPaused\n\nfrom .base import BaseTestCase\nfrom .base import TestModel\nfrom .base import db_loader\nfrom .base import get_sqlite_db\nfrom .base import skip_if\n\n\nget_db = partial(db_loader, 'sqlite', db_class=SqliteQueueDatabase)\ndb = get_sqlite_db()\n\n\nclass User(TestModel):\n    name = TextField(unique=True)\n\n    class Meta:\n        table_name = 'threaded_db_test_user'\n\n\nclass BaseTestQueueDatabase(object):\n    database_config = {}\n    n_rows = 20\n    n_threads = 20\n\n    def setUp(self):\n        super(BaseTestQueueDatabase, self).setUp()\n        User._meta.database = db\n        with db:\n            db.create_tables([User], safe=True)\n\n        User._meta.database = \\\n                self.database = get_db(**self.database_config)\n\n        # Sanity check at startup.\n        self.assertEqual(self.database.queue_size(), 0)\n\n    def tearDown(self):\n        super(BaseTestQueueDatabase, self).tearDown()\n        User._meta.database = db\n        with db:\n            User.drop_table()\n        if not self.database.is_closed():\n            self.database.close()\n        if not db.is_closed():\n            db.close()\n        filename = db.database\n        if os.path.exists(filename):\n            os.unlink(filename)\n\n    def test_query_error(self):\n        self.database.start()\n        curs = self.database.execute_sql('foo bar baz')\n        self.assertRaises(OperationalError, curs.fetchone)\n        self.database.stop()\n\n    def test_integrity_error(self):\n        self.database.start()\n        u = User.create(name='u')\n        self.assertRaises(IntegrityError, User.create, name='u')\n\n    def test_query_execution(self):\n        qr = User.select().execute()\n        self.assertEqual(self.database.queue_size(), 0)\n\n        self.database.start()\n\n        try:\n            users = list(qr)\n            huey = User.create(name='huey')\n            mickey = User.create(name='mickey')\n\n            self.assertTrue(huey.id is not None)\n            self.assertTrue(mickey.id is not None)\n            self.assertEqual(self.database.queue_size(), 0)\n\n        finally:\n            self.database.stop()\n\n    def create_thread(self, fn, *args):\n        raise NotImplementedError\n\n    def create_event(self):\n        raise NotImplementedError\n\n    def test_multiple_threads(self):\n        def create_rows(idx, nrows):\n            for i in range(idx, idx + nrows):\n                User.create(name='u-%s' % i)\n\n        total = self.n_threads * self.n_rows\n        self.database.start()\n        threads = [self.create_thread(create_rows, i, self.n_rows)\n                   for i in range(0, total, self.n_rows)]\n        [t.start() for t in threads]\n        [t.join() for t in threads]\n\n        self.assertEqual(User.select().count(), total)\n        self.database.stop()\n\n    def test_pause(self):\n        event_a = self.create_event()\n        event_b = self.create_event()\n\n        def create_user(name, event, expect_paused):\n            event.wait()\n            if expect_paused:\n                self.assertRaises(WriterPaused, lambda: User.create(name=name))\n            else:\n                User.create(name=name)\n\n        self.database.start()\n\n        t_a = self.create_thread(create_user, 'a', event_a, True)\n        t_a.start()\n        t_b = self.create_thread(create_user, 'b', event_b, False)\n        t_b.start()\n\n        User.create(name='c')\n        self.assertEqual(User.select().count(), 1)\n\n        # Pause operations but preserve the writer thread/connection.\n        self.database.pause()\n\n        event_a.set()\n        self.assertEqual(User.select().count(), 1)\n        t_a.join()\n\n        self.database.unpause()\n        self.assertEqual(User.select().count(), 1)\n\n        event_b.set()\n        t_b.join()\n        self.assertEqual(User.select().count(), 2)\n\n        self.database.stop()\n\n    def test_restart(self):\n        self.database.start()\n        User.create(name='a')\n        self.database.stop()\n        self.database._results_timeout = 0.0001\n\n        self.assertRaises(ResultTimeout, User.create, name='b')\n        self.assertEqual(User.select().count(), 1)\n\n        self.database.start()  # Will execute the pending \"b\" INSERT.\n        self.database._results_timeout = None\n\n        User.create(name='c')\n        self.assertEqual(User.select().count(), 3)\n        self.assertEqual(sorted(u.name for u in User.select()),\n                         ['a', 'b', 'c'])\n\n    def test_waiting(self):\n        D = {}\n\n        def create_user(name):\n            D[name] = User.create(name=name).id\n\n        threads = [self.create_thread(create_user, name)\n                   for name in ('huey', 'charlie', 'zaizee')]\n        [t.start() for t in threads]\n\n        def get_users():\n            D['users'] = [(user.id, user.name) for user in User.select()]\n\n        tg = self.create_thread(get_users)\n        tg.start()\n        threads.append(tg)\n\n        self.database.start()\n        [t.join() for t in threads]\n        self.database.stop()\n\n        self.assertEqual(sorted(D), ['charlie', 'huey', 'users', 'zaizee'])\n\n    def test_next_method(self):\n        self.database.start()\n\n        User.create(name='mickey')\n        User.create(name='huey')\n        query = iter(User.select().order_by(User.name))\n        self.assertEqual(next(query).name, 'huey')\n        self.assertEqual(next(query).name, 'mickey')\n        self.assertRaises(StopIteration, lambda: next(query))\n\n        self.assertEqual(\n            next(self.database.execute_sql('PRAGMA journal_mode'))[0],\n            'wal')\n\n        self.database.stop()\n\n\nclass TestThreadedDatabaseThreads(BaseTestQueueDatabase, BaseTestCase):\n    database_config = {'use_gevent': False}\n\n    def tearDown(self):\n        self.database._results_timeout = None\n        super(TestThreadedDatabaseThreads, self).tearDown()\n\n    def create_thread(self, fn, *args):\n        t = threading.Thread(target=fn, args=args)\n        t.daemon = True\n        return t\n\n    def create_event(self):\n        return threading.Event()\n\n    def test_timeout(self):\n        @self.database.func()\n        def slow(n):\n            time.sleep(n)\n            return 'slept %0.2f' % n\n\n        self.database.start()\n\n        # Make the result timeout very small, then call our function which\n        # will cause the query results to time-out.\n        self.database._results_timeout = 0.001\n        def do_query():\n            # Prepend a space so that we can force it through the threaded\n            # pipeline, otherwise it would execute normally.\n            cursor = self.database.execute_sql(' select slow(?)', (0.01,))\n            self.assertEqual(cursor.fetchone()[0], 'slept 0.01')\n\n        self.assertRaises(ResultTimeout, do_query)\n        self.database.stop()\n\n\n@skip_if(gevent is None, 'gevent not installed')\nclass TestThreadedDatabaseGreenlets(BaseTestQueueDatabase, BaseTestCase):\n    database_config = {'use_gevent': True}\n    n_rows = 10\n    n_threads = 40\n\n    def create_thread(self, fn, *args):\n        return gevent.Greenlet(fn, *args)\n\n    def create_event(self):\n        return GreenEvent()\n"
  },
  {
    "path": "tests/test_utils.py",
    "content": "import functools\n\nfrom .base import ModelTestCase\nfrom .base import TestModel\n\nfrom peewee import *\nfrom playhouse.test_utils import assert_query_count\nfrom playhouse.test_utils import count_queries\n\n\nclass Data(TestModel):\n    key = CharField()\n\n    class Meta:\n        order_by = ('key',)\n\nclass DataItem(TestModel):\n    data = ForeignKeyField(Data, backref='items')\n    value = CharField()\n\n    class Meta:\n        order_by = ('value',)\n\n\nclass TestQueryCounter(ModelTestCase):\n    requires = [DataItem, Data]\n\n    def test_count(self):\n        with count_queries() as count:\n            Data.create(key='k1')\n            Data.create(key='k2')\n\n        self.assertEqual(count.count, 2)\n\n        with count_queries() as count:\n            items = [item.key for item in Data.select().order_by(Data.key)]\n            self.assertEqual(items, ['k1', 'k2'])\n\n            Data.get(Data.key == 'k1')\n            Data.get(Data.key == 'k2')\n\n        self.assertEqual(count.count, 3)\n\n    def test_only_select(self):\n        with count_queries(only_select=True) as count:\n            for i in range(10):\n                Data.create(key=str(i))\n\n            items = [item.key for item in Data.select()]\n            Data.get(Data.key == '0')\n            Data.get(Data.key == '9')\n\n            Data.delete().where(\n                Data.key << ['1', '3', '5', '7', '9']).execute()\n\n            items = [item.key for item in Data.select().order_by(Data.key)]\n            self.assertEqual(items, ['0', '2', '4', '6', '8'])\n\n        self.assertEqual(count.count, 4)\n\n    def test_assert_query_count_decorator(self):\n        @assert_query_count(2)\n        def will_fail_under():\n            Data.create(key='x')\n\n        @assert_query_count(2)\n        def will_fail_over():\n            for i in range(3):\n                Data.create(key=str(i))\n\n        @assert_query_count(4)\n        def will_succeed():\n            for i in range(4):\n                Data.create(key=str(i + 100))\n\n        will_succeed()\n        self.assertRaises(AssertionError, will_fail_under)\n        self.assertRaises(AssertionError, will_fail_over)\n\n    def test_assert_query_count_ctx_mgr(self):\n        with assert_query_count(3):\n            for i in range(3):\n                Data.create(key=str(i))\n\n        def will_fail():\n            with assert_query_count(2):\n                Data.create(key='x')\n\n        self.assertRaises(AssertionError, will_fail)\n\n    @assert_query_count(3)\n    def test_only_three(self):\n        for i in range(3):\n            Data.create(key=str(i))\n"
  },
  {
    "path": "tests/transactions.py",
    "content": "import threading\n\nfrom peewee import *\n\nfrom .base import DatabaseTestCase\nfrom .base import IS_CRDB\nfrom .base import IS_CRDB_NESTED_TX\nfrom .base import IS_MYSQL\nfrom .base import IS_POSTGRESQL\nfrom .base import IS_SQLITE\nfrom .base import ModelTestCase\nfrom .base import db\nfrom .base import new_connection\nfrom .base import skip_if\nfrom .base import skip_unless\nfrom .base_models import Register\n\n\nclass BaseTransactionTestCase(ModelTestCase):\n    requires = [Register]\n\n    def assertRegister(self, vals):\n        query = Register.select().order_by(Register.value)\n        self.assertEqual([register.value for register in query], vals)\n\n    def _save(self, *vals):\n        Register.insert([{Register.value: val} for val in vals]).execute()\n\n\ndef requires_nested(fn):\n    return skip_if(IS_CRDB and not IS_CRDB_NESTED_TX,\n                   'nested transaction support is required')(fn)\n\n\nclass TestTransaction(BaseTransactionTestCase):\n    def test_simple(self):\n        self.assertFalse(db.in_transaction())\n        with db.atomic():\n            self.assertTrue(db.in_transaction())\n            self._save(1)\n\n        self.assertFalse(db.in_transaction())\n        self.assertRegister([1])\n\n        # Explicit rollback, implicit commit.\n        with db.atomic() as txn:\n            self._save(2)\n            txn.rollback()\n            self.assertTrue(db.in_transaction())\n            self._save(3)\n\n        self.assertFalse(db.in_transaction())\n        self.assertRegister([1, 3])\n\n        # Explicit rollbacks.\n        with db.atomic() as txn:\n            self._save(4)\n            txn.rollback()\n            self._save(5)\n            txn.rollback()\n\n        self.assertRegister([1, 3])\n\n    @requires_nested\n    def test_transactions(self):\n        self.assertFalse(db.in_transaction())\n\n        with db.atomic():\n            self.assertTrue(db.in_transaction())\n            self._save(1)\n\n        self.assertRegister([1])\n\n        with db.atomic() as txn:\n            self._save(2)\n            txn.rollback()\n            self._save(3)\n            with db.atomic() as sp1:\n                self._save(4)\n                with db.atomic() as sp2:\n                    self._save(5)\n                    sp2.rollback()\n                with db.atomic() as sp3:\n                    self._save(6)\n                    with db.atomic() as sp4:\n                        self._save(7)\n                        with db.atomic() as sp5:\n                            self._save(8)\n                        self.assertRegister([1, 3, 4, 6, 7, 8])\n                        sp4.rollback()\n\n                    self.assertRegister([1, 3, 4, 6])\n\n        self.assertRegister([1, 3, 4, 6])\n\n    def test_commit_rollback(self):\n        with db.atomic() as txn:\n            self._save(1)\n            txn.commit()\n            self._save(2)\n            txn.rollback()\n\n        self.assertRegister([1])\n\n        with db.atomic() as txn:\n            self._save(3)\n            txn.rollback()\n            self._save(4)\n\n        self.assertRegister([1, 4])\n\n    @requires_nested\n    def test_commit_rollback_nested(self):\n        with db.atomic() as txn:\n            self.test_commit_rollback()\n            txn.rollback()\n        self.assertRegister([])\n\n        with db.atomic():\n            self.test_commit_rollback()\n        self.assertRegister([1, 4])\n\n    def test_nesting_transaction_obj(self):\n        self.assertRegister([])\n\n        with db.transaction() as txn:\n            self._save(1)\n            with db.transaction() as txn2:\n                self._save(2)\n                txn2.rollback()  # Actually issues a rollback.\n                self.assertRegister([])\n            self._save(3)\n        self.assertRegister([3])\n\n        with db.transaction() as txn:\n            self._save(4)\n            with db.transaction() as txn2:\n                with db.transaction() as txn3:\n                    self._save(5)\n                    txn3.commit()  # Actually commits.\n            self._save(6)\n            txn2.rollback()\n\n        self.assertRegister([3, 4, 5])\n\n        with db.transaction() as txn:\n            self._save(6)\n            try:\n                with db.transaction() as txn2:\n                    self._save(7)\n                    raise ValueError()\n            except ValueError:\n                pass\n\n        self.assertRegister([3, 4, 5, 6, 7])\n\n    @requires_nested\n    def test_savepoint_commit(self):\n        with db.atomic() as txn:\n            self._save(1)\n            txn.rollback()\n\n            self._save(2)\n            txn.commit()\n\n            with db.atomic() as sp:\n                self._save(3)\n                sp.rollback()\n\n                self._save(4)\n                sp.commit()\n\n        self.assertRegister([2, 4])\n\n    def test_atomic_decorator(self):\n        @db.atomic()\n        def save(i):\n            self._save(i)\n\n        save(1)\n        self.assertRegister([1])\n\n    def text_atomic_exception(self):\n        def will_fail(self):\n            with db.atomic():\n                self._save(1)\n                self._save(None)\n\n        self.assertRaises(IntegrityError, will_fail)\n        self.assertRegister([])\n\n        def user_error(self):\n            with db.atomic():\n                self._save(2)\n                raise ValueError\n\n        self.assertRaises(ValueError, user_error)\n        self.assertRegister([])\n\n    def test_manual_commit(self):\n        with db.manual_commit():\n            db.begin()\n            self._save(1)\n            db.rollback()\n\n            db.begin()\n            self._save(2)\n            db.commit()\n\n            with db.manual_commit():\n                db.begin()\n                self._save(3)\n                db.rollback()\n\n            db.begin()\n            self._save(4)\n            db.commit()\n\n        self.assertRegister([2, 4])\n\n    def test_mixing_manual_atomic(self):\n        @db.manual_commit()\n        def will_fail():\n            pass\n\n        @db.atomic()\n        def also_fails():\n            pass\n\n        with db.atomic():\n            self.assertRaises(ValueError, will_fail)\n\n        with db.manual_commit():\n            self.assertRaises(ValueError, also_fails)\n\n        with db.manual_commit():\n            with self.assertRaises(ValueError):\n                with db.atomic(): pass\n        with db.atomic():\n            with self.assertRaises(ValueError):\n                with db.manual_commit(): pass\n\n    def test_closing_db_in_transaction(self):\n        with db.atomic():\n            self.assertRaises(OperationalError, db.close)\n\n    @requires_nested\n    def test_db_context_manager(self):\n        db.close()\n        self.assertTrue(db.is_closed())\n\n        with db:\n            self.assertFalse(db.is_closed())\n            self._save(1)\n            with db:\n                self._save(2)\n                try:\n                    with db:\n                        self._save(3)\n                        raise ValueError('xxx')\n                except ValueError:\n                    pass\n                self._save(4)\n\n            try:\n                with db:\n                    self._save(5)\n                    with db:\n                        self._save(6)\n                    raise ValueError('yyy')\n            except ValueError:\n                pass\n\n            self.assertFalse(db.is_closed())\n\n        self.assertTrue(db.is_closed())\n        self.assertRegister([1, 2, 4])\n\n    def test_transaction_concurrency(self):\n        barrier = threading.Barrier(5)\n        accum = []\n\n        def run_thread():\n            barrier.wait(timeout=2)\n            for i in range(10):\n                try:\n                    with db.atomic() as tx:\n                        for j in range(10):\n                            try:\n                                with db.atomic() as sp:\n                                    sp.commit()\n                                    if j % 2 == 0:\n                                        raise ValueError()\n                            except ValueError:\n                                pass\n                        if i % 1 == 0:\n                            raise ValueError()\n                except ValueError:\n                    pass\n            accum.append(True)\n\n        threads = [threading.Thread(target=run_thread) for _ in range(4)]\n        for t in threads: t.start()\n        barrier.wait(timeout=2)\n        for t in threads: t.join()\n\n        self.assertEqual(accum, [True, True, True, True])\n\n\n@requires_nested\nclass TestSession(BaseTransactionTestCase):\n    def test_session(self):\n        self.assertTrue(db.session_start())\n        self.assertTrue(db.session_start())\n        self.assertEqual(db.transaction_depth(), 2)\n\n        self._save(1)\n        self.assertTrue(db.session_commit())\n        self.assertEqual(db.transaction_depth(), 1)\n\n        self._save(2)  # Now we're in autocommit mode.\n        self.assertTrue(db.session_rollback())\n        self.assertEqual(db.transaction_depth(), 0)\n\n        self.assertTrue(db.session_start())\n        self._save(3)\n        self.assertTrue(db.session_rollback())\n        self.assertRegister([1])\n\n    def test_session_with_closed_db(self):\n        db.close()\n        self.assertTrue(db.session_start())\n        self.assertFalse(db.is_closed())\n        self.assertRaises(OperationalError, db.close)\n        self._save(1)\n        self.assertTrue(db.session_rollback())\n        self.assertRegister([])\n\n    def test_session_inside_context_manager(self):\n        with db.atomic():\n            self.assertTrue(db.session_start())\n            self._save(1)\n            self.assertTrue(db.session_commit())\n            self._save(2)\n            self.assertTrue(db.session_rollback())\n            db.session_start()\n            self._save(3)\n\n        self.assertRegister([1, 3])\n\n    def test_commit_rollback_mix(self):\n        db.session_start()\n\n        with db.atomic() as txn:  # Will be a savepoint.\n            self._save(1)\n            with db.atomic() as t2:\n                self._save(2)\n                with db.atomic() as t3:\n                    self._save(3)\n                t2.rollback()\n\n            txn.commit()\n            self._save(4)\n            txn.rollback()\n\n        self.assertTrue(db.session_commit())\n        self.assertRegister([1])\n\n    def test_session_rollback(self):\n        db.session_start()\n\n        self._save(1)\n        with db.atomic() as txn:\n            self._save(2)\n            with db.atomic() as t2:\n                self._save(3)\n\n        self.assertRegister([1, 2, 3])\n        self.assertTrue(db.session_rollback())\n        self.assertRegister([])\n\n        db.session_start()\n        self._save(1)\n\n        with db.transaction() as txn:\n            self._save(2)\n            with db.transaction() as t2:\n                self._save(3)\n                t2.rollback()  # Rolls back everything, starts new txn.\n\n        db.session_commit()\n        self.assertRegister([])\n\n    def test_session_commit(self):\n        db.session_start()\n\n        self._save(1)\n        with db.transaction() as txn:\n            self._save(2)\n            with db.transaction() as t2:\n                self._save(3)\n                t2.commit()  # Saves everything, starts new txn.\n            txn.rollback()\n\n        self.assertTrue(db.session_rollback())\n        self.assertRegister([1, 2, 3])\n\n\n@skip_unless(IS_SQLITE, 'requires sqlite for transaction lock type')\nclass TestTransactionLockType(BaseTransactionTestCase):\n    def test_lock_type(self):\n        db2 = new_connection(timeout=0.0001)\n        db2.connect()\n\n        with self.database.atomic(lock_type='EXCLUSIVE') as txn:\n            with self.assertRaises(OperationalError):\n                with db2.atomic(lock_type='IMMEDIATE') as t2:\n                    self._save(1)\n            self._save(2)\n        self.assertRegister([2])\n\n        with self.database.atomic('IMMEDIATE') as txn:\n            with self.assertRaises(OperationalError):\n                with db2.atomic('EXCLUSIVE') as t2:\n                    self._save(3)\n            self._save(4)\n        self.assertRegister([2, 4])\n\n        with self.database.transaction(lock_type='DEFERRED') as txn:\n            self._save(5)  # Deferred -> Exclusive after our write.\n            with self.assertRaises(OperationalError):\n                with db2.transaction(lock_type='IMMEDIATE') as t2:\n                    self._save(6)\n        self.assertRegister([2, 4, 5])\n\n\nclass TestTransactionIsolationLevel(BaseTransactionTestCase):\n    @skip_unless(IS_POSTGRESQL, 'requires postgresql')\n    def test_isolation_level_pg(self):\n        db2 = new_connection()\n        db2.connect()\n\n        with db2.atomic(isolation_level='SERIALIZABLE'):\n            with db.atomic(isolation_level='SERIALIZABLE'):\n                self._save(1)\n                self.assertDB2(db2, [])\n            self.assertDB2(db2, [])\n        self.assertDB2(db2, [1])\n\n        with db2.atomic(isolation_level='READ COMMITTED'):\n            with db.atomic():\n                self._save(2)\n                self.assertDB2(db2, [1])\n            self.assertDB2(db2, [1, 2])\n        self.assertDB2(db2, [1, 2])\n\n        # NB: Read Uncommitted is treated as Read Committed by PG, so we don't\n        # test it here.\n\n        with db2.atomic(isolation_level='REPEATABLE READ'):\n            with db.atomic(isolation_level='REPEATABLE READ'):\n                self._save(3)\n                self.assertDB2(db2, [1, 2])\n            self.assertDB2(db2, [1, 2])\n        self.assertDB2(db2, [1, 2, 3])\n\n    @skip_unless(IS_MYSQL, 'requires mysql')\n    def test_isolation_level_mysql(self):\n        db2 = new_connection()\n        db2.connect()\n\n        with db2.atomic():\n            with db.atomic(isolation_level='SERIALIZABLE'):\n                self._save(1)\n                self.assertDB2(db2, [])\n            self.assertDB2(db2, [])\n        self.assertDB2(db2, [1])\n\n        with db2.atomic(isolation_level='READ COMMITTED'):\n            with db.atomic():\n                self._save(2)\n                self.assertDB2(db2, [1])\n            self.assertDB2(db2, [1, 2])\n        self.assertDB2(db2, [1, 2])\n\n        with db2.atomic(isolation_level='READ UNCOMMITTED'):\n            with db.atomic():\n                self._save(3)\n                self.assertDB2(db2, [1, 2, 3])\n            self.assertDB2(db2, [1, 2, 3])\n        self.assertDB2(db2, [1, 2, 3])\n\n        with db2.atomic(isolation_level='REPEATABLE READ'):\n            with db.atomic(isolation_level='REPEATABLE READ'):\n                self._save(4)\n                self.assertDB2(db2, [1, 2, 3])\n            self.assertDB2(db2, [1, 2, 3])\n        self.assertDB2(db2, [1, 2, 3, 4])\n\n    def assertDB2(self, db2, vals):\n        with Register.bind_ctx(db2):\n            q = Register.select().order_by(Register.value)\n            self.assertEqual([r.value for r in q], vals)\n"
  }
]