[
  {
    "path": ".arcconfig",
    "content": "{\n  \"project_id\" : \"memsql-spark-connector\",\n  \"conduit_uri\" : \"https:\\/\\/grizzly.internal.memcompute.com\\/api\\/\"\n}\n"
  },
  {
    "path": ".github/workflows/test-and-publish.yml",
    "content": "name: Test and Publish\n\non:\n  pull_request:\n    types: [ opened, synchronize, reopened ]\n  schedule:\n    - cron: \"0 0 * * 0\"\n  push:\n    tags:\n      - \"v*\"\n\njobs:\n  fetch-s2-versions:\n    runs-on: ubuntu-latest\n    outputs:\n      versions: ${{ steps.get_versions.outputs.versions }}\n    steps:\n      - name: Get supported versions of Singlestore\n        id: get_versions\n        uses: singlestore-labs/singlestore-supported-versions@main\n\n  test:\n    needs: fetch-s2-versions\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        singlestore_version: ${{ fromJson(needs.fetch-s2-versions.outputs.versions) }}\n        spark_version: ['3.1.3', '3.2.4', '3.3.4', '3.4.2', '3.5.0', '4.0.0']\n        force_read_from_leaves: ['FALSE']\n        include:\n          - spark_version: '3.1.3'\n            tag: 'ExcludeFromSpark31'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.2.4'\n            tag: 'ExcludeFromSpark32'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.3.4'\n            tag: 'ExcludeFromSpark33'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.4.2'\n            tag: 'ExcludeFromSpark34'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.5.0'\n            tag: 'ExcludeFromSpark35'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '4.0.0'\n            tag: 'ExcludeFromSpark40'\n            scala_version: '2.13.8'\n            java_version: '17'\n          - force_read_from_leaves: 'TRUE'\n            singlestore_version: '9.0'\n            spark_version: '4.0.0'\n            tag: 'ExcludeFromSpark40'\n            scala_version: '2.13.8'\n            java_version: '17'\n    steps:\n      - name: Remove unnecessary pre-installed toolchains for free disk spaces\n        run: |\n          echo \"=== BEFORE ===\"\n          df -h\n          sudo rm -rf /usr/share/dotnet\n          sudo rm -rf /opt/ghc\n          sudo rm -rf /usr/local/share/boost\n          sudo rm -rf \"$AGENT_TOOLSDIRECTORY\"\n          sudo rm -rf /usr/local/lib/android\n          sudo rm -rf /opt/hostedtoolcache/CodeQL\n          sudo rm -rf /opt/hostedtoolcache/Ruby\n          sudo rm -rf /opt/hostedtoolcache/Go\n          docker system prune -af || true\n          sudo apt-get clean\n          echo \"=== AFTER ===\"\n          df -h\n      - uses: actions/checkout@v4\n      - name: Set up test cluster\n        env:\n          SINGLESTORE_LICENSE: ${{ secrets.SINGLESTORE_LICENSE }}\n          ROOT_PASSWORD: ${{ secrets.SINGLESTORE_PASSWORD }}\n          SINGLESTORE_VERSION: ${{ matrix.singlestore_version }}\n        run: ./scripts/setup-cluster.sh\n      - name: Set up JDK\n        uses: actions/setup-java@v4\n        with:\n          java-version: ${{ matrix.java_version }}\n          distribution: 'temurin'\n          cache: sbt\n      - name: Set up sbt launcher\n        uses: sbt/setup-sbt@v1\n      - name: Run tests for Spark ${{ matrix.spark_version }}\n        env:\n          SINGLESTORE_JWT_PASSWORD: ${{ secrets.SINGLESTORE_JWT_PASSWORD }}\n          SINGLESTORE_PASSWORD: ${{ secrets.SINGLESTORE_PASSWORD }}\n          FORCE_READ_FROM_LEAVES: ${{ matrix.force_read_from_leaves }}\n        run: sbt ++${{ matrix.scala_version }} \"testOnly -- -l ${{ matrix.tag }}\" -Dspark.version=${{ matrix.spark_version }}\n\n  publish:\n    needs: test\n    if: startsWith(github.ref, 'refs/tags/v')\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        spark_version: ['3.1.3', '3.2.4', '3.3.4', '3.4.2', '3.5.0', '4.0.0']\n        include:\n          - spark_version: '3.1.3'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.2.4'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.3.4'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.4.2'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '3.5.0'\n            scala_version: '2.12.12'\n            java_version: '11'\n          - spark_version: '4.0.0'\n            scala_version: '2.13.8'\n            java_version: '17'\n\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up JDK\n        uses: actions/setup-java@v4\n        with:\n          java-version: ${{ matrix.java_version }}\n          distribution: 'temurin'\n          cache: sbt\n      - name: Set up sbt launcher\n        uses: sbt/setup-sbt@v1\n      - name: Set up GPG\n        env:\n          ENCRYPTION_KEY: ${{ secrets.ENCRYPTION_KEY }}\n          ENCRYPTION_IV: ${{ secrets.ENCRYPTION_IV }}\n        run: |\n          openssl enc -d -aes-256-cbc -K $ENCRYPTION_KEY -iv $ENCRYPTION_IV -in ci/secring.asc.enc -out ci/secring.asc\n          gpg --import ci/secring.asc\n      - name: Publish Spark ${{ matrix.spark_version }}\n        env:\n          SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}\n          SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}\n        run: |\n          sbt ++${{ matrix.scala_version }} -Dspark.version=${{ matrix.spark_version }} clean publishSigned sonatypeBundleRelease\n"
  },
  {
    "path": ".gitignore",
    "content": "# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm\n# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n\n# User-specific stuff\n/.idea/*\n!/.idea/codeStyles/*\n!/.idea/runConfigurations/*\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n# IntelliJ\n/out/\n/target/\n\n# mpeltonen/sbt-idea plugin\n/.idea_modules/\n\n# File-based project format\n/*.iws\n\n# sbt project stuff\n/project/*\n!/project/build.properties\n!/project/plugins.sbt\n/target\n/build\n/spark-warehouse\n\n/ci/secring.asc\nscripts/ssl"
  },
  {
    "path": ".java-version",
    "content": "1.8\n"
  },
  {
    "path": ".scalafmt.conf",
    "content": "maxColumn = 100\nalign = more"
  },
  {
    "path": "CHANGELOG",
    "content": "2026-04-02 Version 5.0.1\n    * Added customSchema option\n\n2026-04-02 Version 5.0.0\n    * Removed setting of the default server colation\n    * Fixed parallel read from leaves for SingleStore versions 9.0.0 and later\n\n2025-09-24 Version 4.2.2\n    * Restored proper connection closure logic\n\n2025-08-15 Version 4.2.1\n    * Fixed compatibility issue with Databricks 16.4\n\n2025-07-22 Version 4.2.0\n    * Added support of Spark 4.0.0\n    * Changed type conversion for TINYINT to ByteType\n\n2024-11-22 Version 4.1.10\n    * Updated JDBC driver to 1.2.7\n\n2024-11-22 Version 4.1.9\n    * Changed to work with Databricks runtime 15.4\n\n2024-06-14 Version 4.1.8\n    * Changed retry during reading from result table to use exponential backoff\n    * Used ForkJoinPool instead of FixedThreadPool\n    * Added more logging\n\n2024-05-13 Version 4.1.7\n    * Fixed bug that caused reading from the wrong result table when the task was restarted\n\n2024-04-11 Version 4.1.6\n    * Changed LoadDataWriter to send data in batches\n    * Added numPartitions parameter to specify exact number of resulting partition during parallel read\n\n2023-10-05 Version 4.1.5\n    * Added support of Spark 3.5\n    * Updated dependencies\n\n2023-07-18 Version 4.1.4\n    * Added support of Spark 3.4\n    * Added connection attributes\n    * Fixed conflicts of result table names during parallel read\n    * Updated version of the SingleStore JDBC driver\n\n2023-03-31 Version 4.1.3\n    * Updated version of the SingleStore JDBC driver\n    * Fixed error handling when `onDuplicateKeySQL` option is used\n\n2023-02-21 Version 4.1.2\n    * Fixed an issue that would cause a `Table has reached its quota of 1 reader(s)`` error to be displayed when a parallel read was retried\n\n2022-07-13 Version 4.1.1\n    * Added clientEndpoint option for Cloud deployment of the SingleStoreDB\n    * Fixed bug in the error handling that caused deadlock\n    * Added support of the Spark 3.3\n\n2022-06-22 Version 4.1.0\n    * Added support of more SQL expressions in pushdown\n    * Added multi-partition to the parallel read\n    * Updated SingleStore JDBC Driver to 1.1.0\n    * Added JWT authentication\n    * Added connection pooling\n\n2022-01-20 Version 4.0.0\n    * Changed connector to use SingleStore JDBC Driver instead of MariaDB JDBC Driver\n\n2021-12-23 Version 3.2.2\n    * Added possibility to repartition result by columns in parallel read from aggregators\n    * Replaced usages of `transformDown` with `transform` in order to make connector work with Databricks 9.1 LTS\n\n2021-12-14 Version 3.2.1\n    * Added support of the Spark 3.2\n    * Fixed links in the README\n\n2021-11-29 Version 3.2.0\n    * Added support for reading in parallel from aggregator nodes instead of leaf nodes\n\n2021-09-16 Version 3.1.3\n    * Added Spark 3.1 support\n    * Deleted Spark 2.3 and 2.4 support\n\n2021-04-29 Version 3.1.2\n    * Added using external host and port by default while using `useParallelRead`\n\n2021-02-05 Version 3.1.1\n\t* Added support of `com.memsql.spark` data source name for backward compatibility\n\n2021-01-22 Version 3.1.0\n\t* Rebranded `memsql-spark-connector` to `singlestore-spark-connector`\n\t* Spark data source format changed from `memsql` to `singlestore`\n\t* Configuration prefix changed from `spark.datasource.memsql.<config_name>` to `spark.datasource.singlestore.<config_name>`\n\n2020-10-19 Version 3.0.5\n\t* Fixed bug with load balance connections to dml endpoint\n\n2020-09-29 Version 3.1.0-beta\n        * Added Spark 3.0 support\n        * Fixed bugs in pushdowns\n        * Fixed bug with wrong SQL code generation of attribute names that contains special characters\n        * Added methods that allow you to run SQL queries on a MemSQL database directly\n\n2020-08-20 Version 3.0.4\n\t* Added trim pushdown\n\n2020-08-14 Version 3.0.3\n\t* Fixed bug with pushdown of the join condition\n\n2020-08-03 Version 3.0.2\n\t* added maxErrors option\n\t* changed aliases in SQL queries to be more deterministic\n\t* disabled comments inside of the SQL queries when logging level is not TRACE\n\n2020-06-12 Version 3.0.1\n\t* The connector now updates task metrics with the number of records written during write operations\n\n2020-05-27 Version 3.0.0\n\t* Introduces SQL Optimization & Rewrite for most query shapes and compatible expressions\n\t* Implemented as a native Spark SQL plugin\n\t* Supports both the DataSource and DataSourceV2 API for maximum support of current and future functionality\n\t* Contains deep integrations with the Catalyst query optimizer\n\t* Is compatible with Spark 2.3 and 2.4\n\t* Leverages MemSQL LOAD DATA to accelerate ingest from Spark via compression, vectorized cpu instructions, and optimized segment sizes\n\t* Takes advantage of all the latest and greatest features in MemSQL 7.x\n\n2020-05-06 Version 3.0.0-rc1\n        * Support writing into MemSQL reference tables\n        * Deprecated truncate option in favor of overwriteBehavior\n        * New option overwriteBehavior allows you to specify how to overwrite or merge rows during ingest\n        * The Ignore SaveMode now correctly skips all duplicate key errors during ingest\n\n2020-04-30 Version 3.0.0-beta12\n        * Improved performance of new batch insert functionality for `ON DUPLICATE KEY UPDATE` feature\n\n2020-04-30 Version 3.0.0-beta11\n        * Added support for merging rows on ingest via `ON DUPLICATE KEY UPDATE`\n        * Added docker-based demo for running a Zeppelin notebook using the Spark connector\n\n2020-04-20 Version 3.0.0-beta10\n        * Additional functions supported in SQL Pushdown: toUnixTimestamp, unixTimestamp, nextDay, dateDiff, monthsAdd, hypot, rint\n        * Now tested against MemSQL 6.7, and all tests use SSL\n        * Fixed bug with disablePushdown\n\n2020-04-09 Version 3.0.0-beta9\n        * Add null handling to address Spark bug which causes incorrect handling of null literals (https://issues.apache.org/jira/browse/SPARK-31403)\n\n2020-04-01 Version 3.0.0-beta8\n        * Added support for more datetime expressions:\n            * addition/subtraction of datetime objects\n            * to_utc_timestamp, from_utc_timestamp\n            * date_trunc, trunc\n\n2020-03-25 Version 3.0.0-beta7\n        * The connector now respects column selection when loading dataframes into MemSQL\n\n2020-03-24 Version 3.0.0-beta6\n        * Fix bug when you use an expression in an explicit query\n\n2020-03-23 Version 3.0.0-beta5\n        * Increase connection timeout to increase connector reliability\n\n2020-03-20 Version 3.0.0-beta4\n        * Set JDBC driver to MariaDB explicitely to avoid issues with the mysql driver\n\n2020-03-19 Version 3.0.0-beta3\n        * Created tables default to Columnstore\n        * User can override keys attached to new tables\n        * New parallelRead option which enables reading directly from MemSQL leaf nodes\n        * Created tables now set case-sensitive collation on all columns\n          to match Spark semantics\n        * More SQL expressions supported in pushdown (tanh, sinh, cosh)\n\n2020-02-08 Version 3.0.0-beta2\n        * Removed options: masterHost and masterPort\n        * Added ddlEndpoint and ddlEndpoints options\n        * Added path option to support specifying the dbtable via `.load(\"mytable\")` when creating a dataframe\n\n2020-01-30 Version 3.0.0-beta\n        * Full re-write of the Spark Connector\n\n2019-02-27 Version 2.0.7\n        * Add support for EXPLAIN JSON in MemSQL versions 6.7 and later to fix partition pushdown.\n\n2018-09-14 Version 2.0.6\n        * Force utf-8 encoding when loading data into MemSQL\n\n2018-01-18 Version 2.0.5\n        * Explicitly sort MemSQLRDD partitions due to MemSQL 6.0 no longer returning partitions in sorted order by ordinal.\n\n2017-08-31 Version 2.0.4\n        * Switch threads in LoadDataStrategy so that the parent thread reads from the RDD and the new thread writes\n          to MemSQL so that Spark has access to the thread-local variables it expects\n\n2017-07-19 Version 2.0.3\n        * Handle special characters column names in query\n        * Add option to enable jdbc connector to stream result sets row-by-row\n        * Fix groupby queries incorrectly pushed down to leaves\n        * Add option to write to master aggregator only\n        * Add support for reading MemSQL columns of type unsigned bigint and unsigned int\n\n2017-04-17\n        * Pull MemSQL configuration from runtime configuration in sparkSession.conf instead of static config in sparkContext\n        * Fix connection pooling bug where extraneous connections were created\n        * Add MemSQL configuration to disable partition pushdown\n\n2017-02-06 Version 2.0.1\n        * Fixed bug to enable partition pushdown for MemSQL DataFrames loaded from a custom user query\n\n2017-02-01 Version 2.0.0\n        * Compatible with Apache Spark 2.0.0+\n        * Removed experimental strategy SQL pushdown to instead use the more stable Data Sources API for reading\n          data from MemSQL\n        * Removed memsql-spark-interface, memsql-etl\n\n2015-12-15  Version 1.2.1\n        * Python support for extractors and transformers\n        * More extensive SQL pushdown for DataFrame operations\n        * Use DataFrames as common interface between extractor, transformer, and loader\n        * Rewrite connectorLib internals to support SparkSQL relation provider API\n        * Remove RDD.saveToMemSQL\n\n2015-11-19  Version 1.1.1\n        * Set JDBC login timeout to 10 seconds\n\n2015-11-02  Version 1.1.0\n\n\t* Available on Maven Central Repository\n\t* More events for batches\n\t* Deprecated the old Kafka extractor and replaced it with a new one that takes in a Zookeeper quorum address\n\t* Added a new field to pipeline API responses indicating whether or not a pipeline is currently running\n\t* Renamed projects: memsqlsparkinterface -> memsql-spark-interface, memsqletl -> memsql-etl, memsqlrdd -> memsql-connector.\n\t* Robustness and bug fixes\n\n2015-09-24  Version 1.0.0\n\n\t* Initial release of MemSQL Streamliner\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2019 MemSQL (https://www.memsql.com)\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# SingleStoreDB Spark Connector\n## Version: 5.0.1 [![License](http://img.shields.io/:license-Apache%202-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.txt)\n\n## Getting Started\n\nYou can find the latest version of the connector on Maven Central and\nspark-packages.org. The group is `com.singlestore` and the artifact is\n`singlestore-spark-connector_2.11` for Spark 2 and `singlestore-spark-connector_2.12` for Spark 3.\n\n* [Maven Central (Spark 2)](https://search.maven.org/artifact/com.singlestore/singlestore-spark-connector_2.11)\n* [Maven Central (Spark 3)](https://search.maven.org/artifact/com.singlestore/singlestore-spark-connector_2.12)\n* [spark-packages.org](https://spark-packages.org/package/memsql/memsql-spark-connector)\n\nYou can add the connector to your Spark application using: spark-shell, pyspark, or spark-submit\n```\n$SPARK_HOME/bin/spark-shell --packages com.singlestore:singlestore-spark-connector_2.12:5.0.1-spark-4.0.0\n```\n\nWe release multiple versions of the `singlestore-spark-connector`, one for each supported Spark version.\nThe connector follows the `x.x.x-spark-y.y.y` naming convention, where `x.x.x` represents the connector version \nand `y.y.y` represents the corresponding Spark version. \nFor example, in connector `5.0.1-spark-4.0.0`, 4.1.11 is the version of the connector, \ncompiled and tested against Spark version 4.0.0. \nIt is critical to select the connector version that corresponds to the Spark version in use.\n\n## Configuration\n\nThe `singlestore-spark-connector` is configurable globally via Spark options and\nlocally when constructing a DataFrame. The options are named the same, however\nglobal options have the prefix `spark.datasource.singlestore.`.\n\n#### Basic options\n| Option                                              | Default value                                         | Description\n| -                                                   | -                                                     | -\n| `ddlEndpoint`    (On-Premise deployment) (required) | -                                                     | The hostname or IP address of the SingleStoreDB Master Aggregator in the `host[:port]` format, where port is an optional parameter. Example: `master-agg.foo.internal:3308` or `master-agg.foo.internal`.\n| `dmlEndpoints`   (On-Premise deployment)            | ddlEndpoint                                           | The hostname or IP address of SingleStoreDB Aggregator nodes to run queries against in the `host[:port],host[:port],...` format, where :port is an optional parameter (multiple hosts separated by comma). Example: `child-agg:3308,child-agg2`.\n| `clientEndpoint` (Cloud deployment) (required)      | -                                                     | The hostname or IP address to the SingleStoreDB Cloud workspace to run queries against in the format `host[:port]` (port is optional). Ex. `svc-b093ff56-7d9e-499f-b970-7913852facc4-ddl.aws-oregon-2.svc.singlestore.com:3306`\n| `user`                                              | `root`                                                | The SingleStoreDB username.\n| `password`                                          | -                                                     | Password of the SingleStoreDB user.\n| `query`                                             | -                                                     | The query to run (mutually exclusive with dbtable).\n| `dbtable`                                           | -                                                     | The table to query (mutually exclusive with query).\n| `database`                                          | -                                                     | If set, all connections use the specified database by default.\n\n#### Read options\n| Option                                            | Default value                                         | Description\n|---------------------------------------------------|-------------------------------------------------------| -\n| `disablePushdown`                                 | `false`                                               |Disable SQL Pushdown when running queries.\n| `enableParallelRead`                              | `automaticLite`                                       | Enables reading data in parallel for some query shapes. It can have of the following values: `disabled`, `automaticLite`, `automatic`, and `forced`. For more information, see [Parallel Read Support](#parallel-read-support).\n| `customSchema`                                    | -                                                     | Custom schema to use when reading data. Expects a Spark schema in DDL format. To avoid syntax or escaping errors, you can generate this string programmatically using the `toDDL` method on a Spark `StructType`. When set, the connector uses this schema instead of querying the database for metadata, which improves performance when schema inference is expensive or unnecessary. This option is incompatible with SQL pushdown; `disablePushdown` must be set to `true`.\n| `parallelRead.Features`                           | `ReadFromAggregators,ReadFromAggregatorsMaterialized` | Specifies a comma separated list of parallel read features that are tried in the order they are listed. SingleStore supports the following features: `ReadFromLeaves`, `ReadFromAggregators`, and `ReadFromAggregatorsMaterialized`. Example: `ReadFromAggregators,ReadFromAggregatorsMaterialized`. For more information, see [Parallel Read Support](#parallel-read-support).\n| `parallelRead.tableCreationTimeoutMS`             | `0`                                                   | Specifies the amount of time (in milliseconds) the reader waits for the result table creation when using the `ReadFromAggregators` feature. If set to `0`, timeout is disabled.\n| `parallelRead.materializedTableCreationTimeoutMS` | `0`                                                   | Specifies the amount of time (in milliseconds) the reader waits for the result table creation when using the `ReadFromAggregatorsMaterialized` feature. If set to `0`, timeout is disabled.\n| `parallelRead.numPartitions`                      | `0`                                                   | Specifies the exact number of partitions in the resulting DataFrame. If set to `0`, value is ignored.\n| `parallelRead.maxNumPartitions`                   | `0`                                                   | Specifies the Maximum number of partitions in the resulting DataFrame. If set to `0`, no limit is applied.\n| `parallelRead.repartition`                        | `false`                                               | Repartition data before reading.\n| `parallelRead.repartition.columns`                | `RAND()`                                              | Specifies a comma separated list of columns that are used for repartitioning (when `parallelRead.repartition` is enabled). By default, an additional column with `RAND()` value is used for repartitioning.\n\n#### Write options\n| Option                                             | Default value                                         | Description\n| -                                                  | -                                                     | -\n| `overwriteBehavior`                                | `dropAndCreate`                                       | Specifies the behavior during Overwrite. It can have one of the following values: `dropAndCreate`, `truncate`, `merge`.\n| `truncate`                                         | `false`                                               | :warning: **This option is deprecated, please use `overwriteBehavior` instead.** Truncates instead of dropping an existing table during Overwrite.\n| `loadDataCompression`                              | `Gzip`                                                | Compresses data on load. It can have one of the following three values: `GZip`, `LZ4`, and `Skip`.\n| `loadDataFormat`                                   | `CSV`                                                 | Serializes data on load. It can have one of the following values: `Avro` or `CSV`.\n| `tableKey`                                         | -                                                     | Specifies additional keys to add to tables created by the connector. See [Specifying keys for tables created by the Spark Connector](#specifying-keys-for-tables-created-by-the-spark-connector) for more information.\n| `onDuplicateKeySQL`                                | -                                                     | If this option is specified and a new row with duplicate `PRIMARY KEY` or `UNIQUE` index is inserted, SingleStoreDB performs an `UPDATE` operation on the existing row. See [Inserting rows into the table with ON DUPLICATE KEY UPDATE](#inserting-rows-into-the-table-with-on-duplicate-key-update) for more information.\n| `insertBatchSize`                                  | `10000`                                               | Specifies the size of the batch for row insertion.\n| `maxErrors`                                        | `0`                                                   | The maximum number of errors in a single LOAD DATA request. When this limit is reached, the load fails. If this property is set to `0`, no error limit exists.\n| `createRowstoreTable`                              | `rowstore`                                            | If enabled, the connector creates a rowstore table.\n\n#### Connection pool options\n| Option                                             | Default value                                         | Description\n| -                                                  | -                                                     | -\n| `driverConnectionPool.Enabled`                     | `true`                                                | Enables using of connection pool on the driver. (default: `true`)\n| `driverConnectionPool.MaxOpenConns`                | `-1`                                                  | The maximum number of active connections with the same options that can be allocated from the driver pool at the same time, or negative for no limit. (default: `-1`)\n| `driverConnectionPool.MaxIdleConns`                | `8`                                                   | The maximum number of connections with the same options that can remain idle in the driver pool, without extra ones being released, or negative for no limit. (default: `8`)\n| `driverConnectionPool.MinEvictableIdleTimeMs`      | `30000` (30 sec)                                      | The minimum amount of time an object may sit idle in the driver pool before it is eligible for eviction by the idle object evictor (if any). (default: `30000` - 30 sec)\n| `driverConnectionPool.TimeBetweenEvictionRunsMS`   | `1000` (1 sec)                                        | The number of milliseconds to sleep between runs of the idle object evictor thread on the driver. When non-positive, no idle object evictor thread will be run. (default: `1000` - 1 sec)\n| `driverConnectionPool.MaxWaitMS`                   | `-1`                                                  | The maximum number of milliseconds that the driver pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or `-1` to wait indefinitely. (default: `-1`)\n| `driverConnectionPool.MaxConnLifetimeMS`           | `-1`                                                  | The maximum lifetime in milliseconds of a connection. After this time is exceeded the connection will fail the next activation, passivation, or validation test and won’t be returned by the driver pool. A value of zero or less means the connection has an infinite lifetime. (default: `-1`)\n| `executorConnectionPool.Enabled`                   | `true`                                                | Enables using of connection pool on executors. (default: `true`)\n| `executorConnectionPool.MaxOpenConns`              | `true`                                                | The maximum number of active connections with the same options that can be allocated from the executor pool at the same time, or negative for no limit. (default: `true`)\n| `executorConnectionPool.MaxIdleConns`              | `8`                                                   | The maximum number of connections with the same options that can remain idle in the executor pool, without extra ones being released, or negative for no limit. (default: `8`)\n| `executorConnectionPool.MinEvictableIdleTimeMs`    | `2000`                                                | The minimum amount of time an object may sit idle in the executor pool before it is eligible for eviction by the idle object evictor (if any). (default: `2000` - 2 sec)\n| `executorConnectionPool.TimeBetweenEvictionRunsMS` | `1000`                                                | The number of milliseconds to sleep between runs of the idle object evictor thread on the executor. When non-positive, no idle object evictor thread will be run. (default: `1000` - 1 sec)\n| `executorConnectionPool.MaxWaitMS`                 | `-1`                                                  | The maximum number of milliseconds that the executor pool will wait (when there are no available connections) for a connection to be returned before throwing an exception, or `-1` to wait indefinitely. (default: `-1`)\n| `executorConnectionPool.MaxConnLifetimeMS`         | `-1`                                                  | The maximum lifetime in milliseconds of a connection. After this time is exceeded the connection will fail the next activation, passivation, or validation test and won’t be returned by the executor pool. A value of zero or less means the connection has an infinite lifetime. (default: `-1`)\n\n## Examples\n\n### Configure `singlestore-spark-connector` for SingleStoreDB Cloud\nThe following example configures the `singlestore-spark-connector` globally:\n```scala\nspark.conf.set(\"spark.datasource.singlestore.clientEndpoint\", \"singlestore-host\")\nspark.conf.set(\"spark.datasource.singlestore.user\", \"admin\")\nspark.conf.set(\"spark.datasource.singlestore.password\", \"s3cur3-pa$$word\")\n```\n\nThe following example configures the `singlestore-spark-connector` using the read API:\n```scala\nval df = spark.read\n    .format(\"singlestore\")\n    .option(\"clientEndpoint\", \"singlestore-host\")\n    .option(\"user\", \"admin\")\n    .load(\"foo\")\n```\n\nThe following example configures the `singlestore-spark-connector` using an external table in Spark SQL:\n```sql\nCREATE TABLE bar USING singlestore OPTIONS ('clientEndpoint'='singlestore-host','dbtable'='foo.bar')\n```\n\n> note: `singlestore-spark-connector`doesn't support writing to the reference table for SingleStoreDB Cloud\n> note: `singlestore-spark-connector`doesn't support read-only databases for SingleStoreDB Cloud\n\n### Configure `singlestore-spark-connector` for SingleStoreDB On-Premises\nThe following example configures the `singlestore-spark-connector` globally:\n```scala\nspark.conf.set(\"spark.datasource.singlestore.ddlEndpoint\", \"singlestore-master.cluster.internal\")\nspark.conf.set(\"spark.datasource.singlestore.dmlEndpoints\", \"singlestore-master.cluster.internal,singlestore-child-1.cluster.internal:3307\")\nspark.conf.set(\"spark.datasource.singlestore.user\", \"admin\")\nspark.conf.set(\"spark.datasource.singlestore.password\", \"s3cur3-pa$$word\")\n```\n\nThe following example configures the `singlestore-spark-connector` using the read API:\n```scala\nval df = spark.read\n    .format(\"singlestore\")\n    .option(\"ddlEndpoint\", \"singlestore-master.cluster.internal\")\n    .option(\"user\", \"admin\")\n    .load(\"foo\")\n```\n\nThe following example configures the `singlestore-spark-connector` using an external table in Spark SQL:\n```sql\nCREATE TABLE bar USING singlestore OPTIONS ('ddlEndpoint'='singlestore-master.cluster.internal','dbtable'='foo.bar')\n```\n\nFor Java/Python versions of some of these examples, visit the section [\"Java & Python Example\"](#java-python-example)\n\n## Passing Parameters to the SingleStore JDBC Driver\n\nThe SingleStore Spark Connector uses the SingleStore JDBC Driver under the hood. Any configuration options provided to the connector that do not belong to the SingleStore Spark connector specifically (and are not described in the [Configuration](#configuration) section above) are treated as JDBC parameters and are passed directly to the underlying JDBC driver.\n\nYou can find a comprehensive list of the available JDBC driver parameters in the [SingleStore JDBC Driver documentation](https://docs.singlestore.com/cloud/developer-resources/connect-with-application-development-tools/connect-with-java-jdbc/the-singlestore-jdbc-driver/).\n\n### Examples\n\n**Setting JDBC parameters globally:**\n\nYou can set JDBC parameters globally across your Spark session by using the `spark.datasource.singlestore.` prefix, similar to standard connector options.\n\n```scala\n// 'connectTimeout' and 'createDatabaseIfNotExist' are JDBC parameters, not Spark connector options\nspark.conf.set(\"spark.datasource.singlestore.connectTimeout\", \"10000\")\nspark.conf.set(\"spark.datasource.singlestore.createDatabaseIfNotExist\", \"true\")\n```\n\n**Setting JDBC parameters using the Read API:**\n\nYou can also pass JDBC parameters in the `.option()` method when constructing a DataFrame.\n```scala\nval df = spark.read\n    .format(\"singlestore\")\n    .option(\"ddlEndpoint\", \"singlestore-master.cluster.internal\")\n    .option(\"user\", \"admin\")\n    .option(\"connectTimeout\", \"10000\") // This is passed directly to the JDBC driver\n    .option(\"createDatabaseIfNotExist\", \"true\") // This is passed directly to the JDBC driver\n    .load(\"foo\")\n```\n\n## Writing to SingleStoreDB\n\nThe `singlestore-spark-connector` supports saving dataframes to SingleStoreDB using the Spark write API. Here is a basic example of using this API:\n\n```scala\ndf.write\n    .format(\"singlestore\")\n    .option(\"loadDataCompression\", \"LZ4\")\n    .option(\"overwriteBehavior\", \"dropAndCreate\")\n    .mode(SaveMode.Overwrite)\n    .save(\"foo.bar\") // in format: database.table\n```\n\nIf the target table (\"foo\" in the example above) does not exist in SingleStoreDB the\n`singlestore-spark-connector` will automatically attempt to create the table. If you\nspecify SaveMode.Overwrite, if the target table already exists, it will be\nrecreated or truncated before load. Specify `overwriteBehavior = truncate` to truncate rather\nthan re-create.\n\n### Retrieving the number of written rows from taskMetrics\n\nIt is possible to add the listener and get the number of written rows.\n\n```scala\nspark.sparkContext.addSparkListener(new SparkListener() {\n  override def onTaskEnd(taskEnd: SparkListenerTaskEnd) {\n    println(\"Task id: \" + taskEnd.taskInfo.id.toString)\n    println(\"Records written: \" + taskEnd.taskMetrics.outputMetrics.recordsWritten.toString)\n  }\n})\n\ndf.write.format(\"singlestore\").save(\"example\")\n```\n\n### Specifying keys for tables created by the Spark Connector\nWhen creating a table, the `singlestore-spark-connector` will read options prefixed\nwith `tableKey`. These options must be formatted in a specific way in order to\ncorrectly specify the keys.\n\n > :warning: The default table type is a SingleStoreDB columnstore.\n > To create a rowstore table instead, enable the `createRowstoreTable` option.\n\nTo explain we will refer to the following example:\n\n```scala\ndf.write\n    .format(\"singlestore\")\n    .option(\"tableKey.primary\", \"id\")\n    .option(\"tableKey.key.created_firstname\", \"created, firstName\")\n    .option(\"tableKey.unique\", \"username\")\n    .mode(SaveMode.Overwrite)\n    .save(\"foo.bar\") // in format: database.table\n```\n\nIn this example, we are creating three keys:\n1. A primary key on the `id` column\n2. A regular key on the combination of the `firstname` and `created` columns, with the key name `created_firstname`\n3. A unique key on the `username` column\n\nNote on (2): Any key can optionally specify a name, just put it after the key type.\nKey names must be unique.\n\nTo change the default ColumnStore sort key you can specify it explicitly:\n```scala\ndf.write\n    .option(\"tableKey.columnstore\", \"id\")\n```\n\nYou can also customize the shard key like so:\n```scala\ndf.write\n    .option(\"tableKey.shard\", \"id, timestamp\")\n```\n\n## Inserting rows into the table with ON DUPLICATE KEY UPDATE\n\nWhen updating a table it is possible to insert rows with `ON DUPLICATE KEY UPDATE` option.\nSee [sql reference](https://docs.singlestore.com/db/latest/en/reference/sql-reference/data-manipulation-language-dml/insert.html) for more details.\n> :warning: This feature doesn't work for columnstore tables with SingleStoreDB 7.1.\n```scala\ndf.write\n    .option(\"onDuplicateKeySQL\", \"age = age + 1\")\n    .option(\"insertBatchSize\", 300)\n    .mode(SaveMode.Append)\n    .save(\"foo.bar\")\n```\n\nAs a result of the following query, all new rows will be appended without changes.\nIf a row with the same `PRIMARY KEY` or `UNIQUE` index already exists then the corresponding `age` value will be increased.\n\nWhen you use ON DUPLICATE KEY UPDATE, all rows of the DataFrame are split into batches, and every insert query will contain no more than the specified `insertBatchSize` rows setting.\n\n## Save Modes\n\nSave operations can optionally take a SaveMode, that specifies how to handle existing data if present.\nIt is important to realize that these save modes do not utilize any locking and are not atomic.\n\n1. `SaveMode.Append` means that when saving a DataFrame to a data source, if data/table already exists,\ncontents of the DataFrame are expected to be appended to existing data.\n2. `SaveMode.Overwrite` means that when saving a DataFrame to a data source,\nif data/table already exists, existing data is expected to be overwritten by the contents of the DataFrame.\n> `Overwrite` mode depends on `overwriteBehavior` option, for better understanding look at the section [\"Merging on save\"](#merging-on-save)\n3. `SaveMode.ErrorIfExists` means that when saving a DataFrame to a data source,\nif data already exists, an exception is expected to be thrown.\n4. `SaveMode.Ignore` means that when saving a DataFrame to a data source, if data already exists, \ncontents of the DataFrame are expected to be appended to existing data and all rows with duplicate key are ignored.\n\n### Example of `SaveMode` option\n\n```scala\ndf.write\n    .mode(SaveMode.Append)\n    .save(\"foo.bar\")\n```\n\n<h2 id=\"merging-on-save\">Merging on save</h2>\n\nWhen saving dataframes or datasets to SingleStoreDB, you can manage how SaveMode.Overwrite is interpreted by the connector via the option overwriteBehavior.\nThis option can take one of the following values:\n\n1. `dropAndCreate`(default) - drop and create the table before writing new values.\n2. `truncate` - truncate the table before writing new values.\n3. `merge` - replace rows with new rows by matching on the primary key.\n(Use this option only if you need to fully rewrite existing rows with new ones.\nTo specify some rule for the update, use the `onDuplicateKeySQL` option instead.)\n\nAll these options are case-insensitive.\n\n### Example of `merge` option\n\nSuppose you have the following table, and the `Id` column is the primary key.\n\n`SELECT * FROM <table>;`\n\n| Id    | Name          | Age |\n| ----- |:-------------:| ---:|\n| 1     | Alice         | 20  |\n| 2     | Bob           | 25  |\n| 3     | Charlie       | 30  |\n\nIf you save the following dataframe with `overwriteBehavior = merge`:\n\n| Id    | Name          | Age |\n| ----- |:-------------:| ---:|\n| 2     | Daniel        | 22  |\n| 3     | Eve           | 27  |\n| 4     | Franklin      | 35  |\n\n```scala\ndf.write\n    .format(\"singlestore\")\n    .option(\"overwriteBehavior\", \"merge\")\n    .mode(SaveMode.Overwrite)\n    .save(\"<yourdb>.<table>\")\n```\n\nAfter the save is complete, the table will look like this:\n> note: rows with Id=2 and Id=3 were overwritten with new rows <br />\n> note: the row with Id=1 was not touched and still exists in the result\n\n`SELECT * FROM <table>;`\n\n| Id    | Name          | Age |\n| ----- |:-------------:| ---:|\n| 1     | Alice         | 20  |\n| 2     | Daniel        | 22  |\n| 3     | Eve           | 27  |\n| 4     | Franklin      | 35  |\n\n## SQL Pushdown\n\nThe `singlestore-spark-connector` has extensive support for rewriting Spark SQL\nand dataframe operation query plans into standalone SingleStoreDB queries. \nThis allows most of the computation to be pushed into the SingleStoreDB distributed system \nwithout any manual intervention. The SQL rewrites are enabled automatically, \nbut they can be disabled using the `disablePushdown` option.\nThe `singlestore-spark-connector` also support partial pushdown, \nwhere certain parts of a query can be evaluated in SingleStoreDB \nand certain parts can be evaluated in Spark.\n\n> :warning: SQL Pushdown is either enabled or disabled on the *entire* Spark\n> Session. If you want to run multiple queries in parallel with different\n> values of `disablePushdown`, make sure to run them on separate Spark Sessions.\n\nWe currently support most of the primary Logical Plan nodes in Spark SQL\nincluding:\n\n * Project\n * Filter\n * Aggregate\n * Window\n * Join\n * Limit\n * Sort\n\nWe also support most Spark SQL expressions. A full list of supported\noperators/functions can be found in the\n[ExpressionGen.scala](src/main/scala/com/singlestore/spark/ExpressionGen.scala) file.\n\nThe best place to look for examples of fully supported queries is in the tests.\nCheck out this file as a starting point:\n[SQLPushdownTest.scala](src/test/scala/com/singlestore/spark/SQLPushdownTest.scala).\n\n### Debugging SQL Pushdown\n\nIf you encounter an issue with SQL Pushdown the first step is to look at the\nexplain. You can do this easily from any dataframe using the function\n`df.explain()`. If you pass the argument `true` you will get a lot more output\nthat includes pre and post optimization passes.\n\nIn addition, the `singlestore-spark-connector` outputs a lot of helpful information\nwhen the TRACE log level is enabled for the `com.singlestore.spark` package.\nTo enable TRACE log level, add the following line(s) to the log4j configuration:\n\n - Log4j\n```\nlog4j.logger.com.singlestore.spark=TRACE\n```\n\n - Log4j 2\n```\nlogger.singlestore.name = com.singlestore.spark\nlogger.singlestore.level = TRACE\nlogger.singlestore.additivity = false\n```\n\nMake sure not to leave it in place since it generates a huge amount of tracing\noutput.\n\n## SQL Pushdown Incompatibilities\n* `ToUnixTimestamp` and `UnixTimestamp` only handle time values less than `2038-01-19 03:14:08`, if they get `DateType` or `TimestampType` as a first argument.\n* `FromUnixTime` with `yyyy-MM-dd HH:mm:ss` as the default format, only handles time less than `2147483648` (`2^31`).\n* `DecimalType` is truncated on overflow (by default, Spark either throws an exception or returns null).\n* `greatest` and `least` return `null` if at least one argument is `null` (in Spark these functions skip nulls).\n* When a value can not be converted to numeric or fractional type SingleStoreDB returns 0 (Spark returns `null`).\n* `Atanh(x)`, for x ∈ (-∞, -1] ∪ [1, ∞) returns, `null` (Spark returns `NaN`).\n* When a string is cast to a numeric type, SingleStoreDB takes the prefix of it which is numeric (Spark returns `null` if the whole string is not numeric).\n* When a numeric type is cast to a smaller one (in size), SingleStoreDB truncates it. For example `500` cast to the `Byte` will be `127`.\n  Note: Spark optimizer can optimize casts for literals and then the behaviour for literals matches custom Spark behaviour.\n* When a fractional type is cast to an integral type, SingleStoreDB rounds it to the closest value.\n* `Log` returns `null` instead of `NaN`, `Infinity`, `-Infinity`.\n* `Round` rounds down if the number to be rounded is followed by 5, and it is `DOUBLE` or `FLOAT` (`DECIMAL` is rounded up).\n* `Conv` works differently if the number contains non-alphanumeric characters.\n* `ShiftLeft`, `ShiftRight`, and `ShiftRightUnsigned` convert the value to an `UNSIGNED BIGINT` and then produce the shift.\n  In case of an overflow, it returns `0` (`1<<64` = `0` and `10>>20` = `0`).\n* `BitwiseGet` returns 0 when the bit position is negative or exceeds the bit upper limit.\n* `Initcap` defines a letter as the beginning of a word even if it is enclosed in quotation marks, brackets, etc. For example \"dear sir/madam (miss)\" is converted to \"Dear Sir/Madam (Miss)\".\n* `Skewness(x)`, in Spark 3.0, for `STD(x) = 0` returns `null` instead of `NaN`.\n\n## Parallel Read Support\nParallel read can be enabled using `enableParallelRead` option. This can drastically improve performance in some cases.\n\nThe `enableParallelRead` option can have one of the following values:\n * `disabled`: Disables parallel reads and performs non-parallel reads.\n * `automaticLite`: Performs parallel reads if at least one parallel read feature specified in `parallelRead.Features` is supported. \nOtherwise performs a non-parallel read. In `automaticLite` mode, after push down of the outer sorting operation\n(for example, a nested `SELECT` statement where sorting is done in a top-level `SELECT`) into SingleStoreDB is done, a non-parallel read is used.\n * `automatic`: Performs parallel reads if at least one parallel read feature specified in `parallelRead.Features` is supported. \nOtherwise performs a non-parallel read. In `automatic` mode, the `singlestore-spark-connector` is unable to push down an outer sorting operation into SingleStore. \nFinal sorting is done at the Spark end of the operation.\n * `forced`: Performs parallel reads if at least one parallel read feature specified in `parallelRead.Features` is supported. \nOtherwise it returns an error. In `forced` mode, the `singlestore-spark-connector` is unable to push down an outer sorting operation into SingleStore. \nFinal sorting is done at the Spark end of the operation.\n\nBy default, `enableParallelRead` is set to `automaticLite`.\n\n### Parallel read features\nThe SingleStoreDB Spark Connector supports the following parallel read features:\n * `readFromAggregators`\n * `readFromAggregatorsMaterialized`\n * `readFromLeaves`\n\nThe connector uses the first feature specified in `parallelRead.Features` which meets all the requirements. \nThe requirements for each feature are specified below. \nBy default, the connector uses the `readFromAggregators` feature. \nYou can repartition the result set for `readFromAggregators` and `readFromAggregatorsMaterialized` features. \nSee [Parallel Read Repartitioning](#parallel-read-repartitioning) for more information.\n\n#### readFromAggregators\nWhen this feature is used, the `singlestore-spark-connector` will use [SingleStoreDB parallel read functionality](https://docs.singlestore.com/db/latest/en/query-data/query-procedures/read-query-results-in-parallel.html).\nBy default, the number of partitions in the resulting DataFrame is the least of the number of partitions in the SingleStoreDB database and Spark parallelism level\n(i.e., sum of `(spark.executor.cores/spark.task.cpus)` for all executors).\nNumber of partitions in the resulting DataFrame can be controlled using `parallelRead.maxNumPartitions` and `parallelRead.numPartitions` options.\nTo use this feature, all reading queries must start at the same time. \nConnector tries to retrieve maximum number of tasks that can be run concurrently and uses this value to distribute reading queries.\nIn some cases, connector is not able to retrieve this value (for example, with AWS Glue). In such cases, `parallelRead.numPartitions` option is required.\n\nUse the `parallelRead.tableCreationTimeoutMS` option to specify a timeout for result table creation.\n\nRequirements:\n * SingleStoreDB version 7.5+\n * Either the `database` option is set, or the database name is specified in the `load` option\n * SingleStoreDB parallel read functionality supports the generated query\n * `parallelRead.numPartitioins` option is set, or connector is able to compute maximum number of concurrent tasks that can be run on Spark cluster\n\n#### readFromAggregatorsMaterialized\nThis feature is very similar to `readFromAggregators`. The only difference is that `readFromAggregatorsMaterialized` uses the \n`MATERIALIZED` option to create the result table. When this feature is used, the reading tasks do not have to start at the same time. \nHence, the parallelism level on the Spark cluster does not affect the reading tasks. \nAlthough, using the `MATERIALIZED` option may cause a query to fail if SingleStoreDB does not have enough memory to materialize the result set.\nBy default, the number of partitions in the resulting DataFrame is equal to the number of partitions in the SingleStoreDB database.\nNumber of partitions in the resulting DataFrame can be controlled using `parallelRead.maxNumPartitions` and `parallelRead.numPartitions` options.\n\nUse the `parallelRead.materializedTableCreationTimeoutMS` option to specify a timeout for materialized result table creation.\n\nRequirements:\n * SingleStoreDB version 7.5+\n * Either the `database` option is set, or the database name is specified in the `load` option\n * SingleStoreDB parallel read functionality supports the generated query\n\n#### readFromLeaves\nWhen this feature is used, the `singlestore-spark-connector` skips the transaction layer and reads directly from partitions on the leaf nodes.\nHence, each individual read task sees an independent version of the database's distributed state. \nIf some queries (other than read operation) are run on the database, they may affect the current read operation.\nMake sure to take this into account when using `readFromLeaves` feature.\n\nThis feature supports only those query-shapes that do not perform any operation on the aggregator and can be pushed down to the leaf nodes.\n\nIn order to use `readFromLeaves` feature, the username and password provided to the\n`singlestore-spark-connector` must be the same across all nodes in the cluster.\n\nBy default, the number of partitions in the resulting DataFrame is equal to the number of partitions in the SingleStoreDB database.\nNumber of partitions in the resulting DataFrame can be controlled using `parallelRead.maxNumPartitions` and `parallelRead.numPartitions` options.\n\nRequirements:\n * Either the `database` option is set, or the database name is specified in the `load` option\n * The username and password provided to the `singlestore-spark-connector` must be uniform across all the nodes in the cluster, \nbecause parallel reads require consistent authentication and connectible leaf nodes\n * The hostnames and ports listed by `SHOW LEAVES` must be directly connectible from Spark\n * The generated query can be pushed down to the leaf nodes\n\n### Parallel read repartitioning\nYou can repartition the result using `parallelRead.repartition` option for the `readFromAggregators` and `readFromAggregatorsMaterialized` features \nto ensure that each task reads approximately the same amount of data.\nThis option is very useful for queries with top level limit clauses as without repartitioning it is possible that all rows will belong to one partition.\n\nUse the `parallelRead.repartition.columns` option to specify a comma separated list of columns that will be used for repartitioning.\nColumn names that contain leading or trailing whitespaces or commas must be escaped as:\n - Column name must be enclosed in backticks\n```\n\"a\" -> \"`a`\"\n```\n - Each backtick (`) in the column name must be replaced with two backticks (``)\n```\n\"a`a``\" -> \"a``a````\"\n```\n\nBy default, repartitioning is done using an additional column with `RAND()` value.\n\n### Example\n```scala\nspark.read.format(\"singlestore\")\n.option(\"enableParallelRead\", \"automatic\")\n.option(\"parallelRead.Features\", \"readFromAggregators,readFromLeaves\")\n.option(\"parallelRead.repartition\", \"true\")\n.option(\"parallelRead.repartition.columns\", \"a, b\")\n.option(\"parallelRead.TableCreationTimeout\", \"1000\")\n.load(\"db.table\")\n```\n\nIn the following example, connector will check requirements for `readFromAggregators`.\nIf they are satisfied, it will use this feature.\nOtherwise, it will check requirements for `readFromLeaves`.\nIf they are satisfied, connector will use this feature. Otherwise, it will use non-parallel read.\nIf the connector uses `readFromAggregators`, it will repartition the result on the SingleStoreDB side before reading it,\nand it will fail if creation of the result table will take longer than `1000` milliseconds.\n\n## Running SQL queries\nThe methods `executeSinglestoreQuery(query: String, variables: Any*)` and `executeSinglestoreQueryDB(db: String, query: String, variables: Any*)`\nallow you to run SQL queries on a SingleStoreDB database directly using the existing `SparkSession` object. The method `executeSinglestoreQuery`\nuses the database defined in the `SparkContext` object you use. `executeSinglestoreQueryDB` allows you to specify the database that\nwill be used for querying.\nThe following examples demonstrate their usage (assuming you already have\ninitialized `SparkSession` object named `spark`). The methods return `Iterator[org.apache.spark.sql.Row]` object.\n\n```scala\n// this imports the implicit class QueryMethods which adds the methods\n// executeSinglestoreQuery and executeSinglestoreQueryDB to SparkSession class\nimport com.singlestore.spark.SQLHelper.QueryMethods\n\n// You can pass an empty database to executeSinglestoreQueryDB to connect to SingleStoreDB without specifying a database.\n// This allows you to create a database which is defined in the SparkSession config for example.\nspark.executeSinglestoreQueryDB(\"\", \"CREATE DATABASE foo\")\n// the next query can be used if the database field has been specified in spark object\ns = spark.executeSinglestoreQuery(\"CREATE TABLE user(id INT, name VARCHAR(30), status BOOLEAN)\")\n\n// you can create another database\nspark.executeSinglestoreQuery(\"CREATE DATABASE bar\")\n// the database specified as the first argument will override the database set in the SparkSession object\ns = spark.executeSinglestoreQueryDB(\"bar\", \"CREATE TABLE user(id INT, name VARCHAR(30), status BOOLEAN)\")\n```\n\nYou can pass query parameters to the functions as arguments following `query`. The supported types for parameters are `String, Int, Long, Short, Float, Double, Boolean, Byte, java.sql.Date, java.sql.Timestamp`.\n```scala\nimport com.singlestore.spark.SQLHelper.QueryMethods\n\nvar userRows = spark.executeSinglestoreQuery(\"SELECT id, name FROM USER WHERE id > ? AND status = ? AND name LIKE ?\", 10, true, \"%at%\")\n\nfor (row <- userRows) {\n  println(row.getInt(0), row.getString(1))\n}\n```\nAlternatively, these functions can take `SparkSession` object as the first argument, as in the example below\n```scala\nimport com.singlestore.spark.SQLHelper.{executeSinglestoreQuery, executeSinglestoreQueryDB}\n\nexecuteSinglestoreQuery(spark, \"CREATE DATABASE foo\")\nvar s = executeSinglestoreQueryDB(spark, \"foo\", \"SHOW TABLES\")\n```\n## Security\n\n### SQL Permissions\n\nThe [permission matrix](https://docs.singlestore.com/db/latest/en/reference/sql-reference/security-management-commands/permissions-matrix.html)\ndescribes the permissions required to run each command.\n\nTo perform any SQL operation through the SingleStore Spark Connector, \nyou must have the permissions required for that specific operation. \nThe following matrix describes the minimum permissions required to perform some operations.\n> Note: The ALL PRIVILEGES permission allows you to perform any operation.\n\n| Operation                       | Min. Permission          | Alternative Permission |\n| ------------------------------- |:------------------------:| ----------------------:|\n| `READ` from collection          | `SELECT`                 | `ALL PRIVILEGES`       |\n| `WRITE` to collection           | `SELECT, INSERT`         | `ALL PRIVILEGES`       |\n| `DROP` database or collection   | `SELECT, INSERT, DROP`   | `ALL PRIVILEGES`       |\n| `CREATE` database or collection | `SELECT, INSERT, CREATE` | `ALL PRIVILEGES`       |\n\nFor more information on granting privileges, see [GRANT](https://docs.singlestore.com/db/latest/en/reference/sql-reference/security-management-commands/grant.html)\n\n### Connecting with a Kerberos-authenticated User\n\nYou can use the SingleStoreDB Spark Connector with a Kerberized user without any additional configuration.\nTo use a Kerberized user, you need to configure the connector with the given SingleStoreDB database user that is authenticated with Kerberos\n(via the `user` option). Please visit our documentation [here](https://docs.singlestore.com/db/latest/en/security/authentication/kerberos-authentication.html)\nto learn about how to configure SingleStoreDB users with Kerberos.\n\nHere is an example of configuring the Spark connector globally with a Kerberized SingleStoreDB user called `krb_user`.\n\n```scala\nspark = SparkSession.builder()\n    .config(\"spark.datasource.singlestore.user\", \"krb_user\")\n    .getOrCreate()\n```\n\nYou do not need to provide a password when configuring a Spark Connector user that is Kerberized.\nThe connector driver (SingleStoreDB JDBC driver) will be able to authenticate the Kerberos user from the cache by the provided username.\nOther than omitting a password with this configuration, using a Kerberized user with the Connector is no different than using a standard user.\nNote that if you do provide a password, it will be ignored.\n\n### SSL Support\n\nThe SingleStoreDB Spark Connector uses the SingleStoreDB JDBC Driver under the hood and thus\nsupports SSL configuration out of the box. In order to configure SSL, first\nensure that your SingleStoreDB cluster has SSL configured. Documentation on how to set\nthis up can be found here:\nhttps://docs.singlestore.com/latest/guides/security/encryption/ssl/\n\nOnce you have setup SSL on your server, you can enable SSL via setting the following options:\n\n```scala\nspark.conf.set(\"spark.datasource.singlestore.useSSL\", \"true\")\nspark.conf.set(\"spark.datasource.singlestore.serverSslCert\", \"PATH/TO/CERT\")\n```\n\n**Note:** the `serverSslCert` option may be server's certificate in DER form, or the server's\nCA certificate. Can be used in one of 3 forms:\n\n* `serverSslCert=/path/to/cert.pem` (full path to certificate)\n* `serverSslCert=classpath:relative/cert.pem` (relative to current classpath)\n* or as verbatim DER-encoded certificate string `------BEGIN CERTIFICATE-----...`\n\nYou may also want to set these additional options depending on your SSL configuration:\n\n```scala\nspark.conf.set(\"spark.datasource.singlestore.trustServerCertificate\", \"true\")\nspark.conf.set(\"spark.datasource.singlestore.disableSslHostnameVerification\", \"true\")\n```\n\nSee [The SingleStoreDB JDBC Driver](https://docs.singlestore.com/db/latest/en/developer-resources/connect-with-application-development-tools/connect-with-java-jdbc/the-singlestore-jdbc-driver.html#tls-parameters) \nfor more information.\n\n### JWT authentication\n\nYou may authenticate your connection to the SingleStoreDB cluster using the SingleStoreDB Spark connector with a JWT.\nTo use JWT-based authentication, specify the following parameters:\n - `credentialType=JWT` \n - `password=<jwt-token>`\n\nHere's a sample configuration that uses JWT-based authentication:\n```\nSparkConf conf = new SparkConf();\nconf.set(\"spark.datasource.singlestore.ddlEndpoint\", \"singlestore-master.cluster.internal\")\nconf.set(\"spark.datasource.singlestore.dmlEndpoints\", \"singlestore-master.cluster.internal,singlestore-child-1.cluster.internal:3307\")\nconf.set(\"spark.datasource.singlestore.credentialType\", \"JWT\")\nconf.set(\"spark.datasource.singlestore.useSsl\", \"true\")\nconf.set(\"spark.datasource.singlestore.user\", \"s2user\")\nconf.set(\"spark.datasource.singlestore.password\", \"eyJhbGci.eyJzdWIiOiIxMjM0NTY3.masHf\")\n```\n\n> note: To authenticate your connection to the SingleStoreDB cluster using the SingleStoreDB Spark connector with a JWT,\n> the SingleStoreDB user must connect via SSL and use a JWT for authentication.\n>\n> See [Create a JWT User](https://docs.singlestore.com/managed-service/en/security/authentication/authenticate-via-jwt.html#create-a-jwt-user-751086) for more information.\n\nSee [Authenticate via JWT](https://docs.singlestore.com/managed-service/en/security/authentication/authenticate-via-jwt.html) for more information.\n\n## Filing issues\n\nWhen filing issues please include as much information as possible as well as any\nreproduction steps. It's hard for us to reproduce issues if the problem depends\non specific data in your SingleStoreDB table for example. Whenever possible please try\nto construct a minimal reproduction of the problem and include the table\ndefinition and table contents in the issue.\n\nIf the issue is related to SQL Pushdown (or you aren't sure) make sure to\ninclude the TRACE output (from the com.singlestore.spark package) or the full explain\nof the plan. See the debugging SQL Pushdown section above for more information\non how to do this.\n\nHappy querying!\n\n## Setting up development environment\n\n * install Oracle JDK 8 from this url: https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html\n * install the community edition of Intellij IDEA from https://www.jetbrains.com/idea/\n * clone the repository https://github.com/memsql/singlestore-spark-connector.git\n * in Intellij IDEA choose `Configure->Plugins` and install Scala plugin\n * in Intellij IDEA run `Import Project` and select path to singlestore-spark-connector `build.sbt` file\n * choose `import project from external model` and `sbt`\n * in `Project JDK` select `New...->JDK` and choose the path to the installed JDK\n * `Finish`\n * it will overwrite some files and create build files (which are in .gitignore)\n * you may need to remove the `.idea` directory for IDEA to load the project properly\n * in Intellij IDEA choose `File->Close Project`\n * run `git checkout .` to revert all changes made by Intellij IDEA\n * in Intellij IDEA choose `Open` and select path to singlestore-spark-connector\n * run `Test Spark 3.0` (it should succeed)\n\n<h2 id=\"java-python-example\">Java & Python Examples</h2>\n\n### Java\n\n#### Configuration\n\n```\nSparkConf conf = new SparkConf();\nconf.set(\"spark.datasource.singlestore.ddlEndpoint\", \"singlestore-master.cluster.internal\")\nconf.set(\"spark.datasource.singlestore.dmlEndpoints\", \"singlestore-master.cluster.internal,singlestore-child-1.cluster.internal:3307\")\nconf.set(\"spark.datasource.singlestore.user\", \"admin\")\nconf.set(\"spark.datasource.singlestore.password\", \"s3cur3-pa$$word\")\n```\n\n#### Read Data\n\n```\nDataFrame df = spark\n  .read()\n  .format(\"singlestore\")\n  .option(\"ddlEndpoint\", \"singlestore-master.cluster.internal\")\n  .option(\"user\", \"admin\")\n  .load(\"foo\");\n```\n\n#### Write Data\n\n```\ndf.write()\n    .format(\"singlestore\")\n    .option(\"loadDataCompression\", \"LZ4\")\n    .option(\"overwriteBehavior\", \"dropAndCreate\")\n    .mode(SaveMode.Overwrite)\n    .save(\"foo.bar\")\n```\n\n### Python\n\n#### Configuration\n\n```\nspark.conf.set(\"spark.datasource.singlestore.ddlEndpoint\", \"singlestore-master.cluster.internal\")\nspark.conf.set(\"spark.datasource.singlestore.dmlEndpoints\", \"singlestore-master.cluster.internal,singlestore-child-1.cluster.internal:3307\")\nspark.conf.set(\"spark.datasource.singlestore.user\", \"admin\")\nspark.conf.set(\"spark.datasource.singlestore.password\", \"s3cur3-pa$$word\")\n```\n\n#### Read Data\n\n```\ndf = spark \\\n  .read \\\n  .format(\"singlestore\") \\\n  .option(\"ddlEndpoint\", \"singlestore-master.cluster.internal\") \\\n  .option(\"user\", \"admin\") \\\n  .load(\"foo\")\n```\n\n#### Write Data\n\n```\ndf.write \\\n    .format(\"singlestore\") \\\n    .option(\"loadDataCompression\", \"LZ4\") \\\n    .option(\"overwriteBehavior\", \"dropAndCreate\") \\\n    .mode(\"overwrite\") \\\n    .save(\"foo.bar\")\n```\n"
  },
  {
    "path": "build.sbt",
    "content": "import xerial.sbt.Sonatype._\n\n/*\n  To run tests or publish with a specific spark version use this java option:\n    -Dspark.version=3.0.0\n */\nval sparkVersion = sys.props.get(\"spark.version\").getOrElse(\"4.0.0\")\nval scalaVersionStr = sparkVersion match {\n  case \"3.1.3\" => \"2.12.12\"\n  case \"3.2.4\" => \"2.12.12\"\n  case \"3.3.4\" => \"2.12.12\"\n  case \"3.4.2\" => \"2.12.12\"\n  case \"3.5.0\" => \"2.12.12\"\n  case \"4.0.0\" => \"2.13.8\"\n}\nval scalaVersionPrefix = scalaVersionStr.substring(0, 4)\nval jacksonDatabindVersion = sparkVersion match {\n  case \"3.1.3\" => \"2.10.0\"\n  case \"3.2.4\" => \"2.12.3\"\n  case \"3.3.4\" => \"2.13.4.2\"\n  case \"3.4.2\" => \"2.14.2\"\n  case \"3.5.0\" => \"2.15.2\"\n  case \"4.0.0\" => \"2.18.2\"\n}\n\nlazy val root = project\n  .withId(\"singlestore-spark-connector\")\n  .in(file(\".\"))\n  .enablePlugins(BuildInfoPlugin)\n  .settings(\n    name := \"singlestore-spark-connector\",\n    organization := \"com.singlestore\",\n    scalaVersion := scalaVersionStr,\n    Compile / unmanagedSourceDirectories += (Compile / sourceDirectory).value / (sparkVersion match {\n      case \"3.1.3\" => \"scala-sparkv3.1\"\n      case \"3.2.4\" => \"scala-sparkv3.2\"\n      case \"3.3.4\" => \"scala-sparkv3.3\"\n      case \"3.4.2\" => \"scala-sparkv3.4\"\n      case \"3.5.0\" => \"scala-sparkv3.5\"\n      case \"4.0.0\" => \"scala-sparkv4.0\"\n    }),\n    version := s\"5.0.1-spark-${sparkVersion}\",\n    licenses += \"Apache-2.0\" -> url(\n      \"http://opensource.org/licenses/Apache-2.0\"\n    ),\n    resolvers += \"Spark Packages Repo\" at \"https://dl.bintray.com/spark-packages/maven\",\n    libraryDependencies ++= Seq(\n      // runtime dependencies\n      \"org.apache.spark\"       %% \"spark-core\"             % sparkVersion % \"provided, test\",\n      \"org.apache.spark\"       %% \"spark-sql\"              % sparkVersion % \"provided, test\",\n      \"org.apache.avro\"        % \"avro\"                    % \"1.11.3\",\n      \"org.apache.commons\"     % \"commons-dbcp2\"           % \"2.7.0\",\n      \"org.scala-lang.modules\" %% \"scala-java8-compat\"     % \"0.9.0\",\n      \"com.singlestore\"        % \"singlestore-jdbc-client\" % \"1.2.7\",\n      \"io.spray\"               %% \"spray-json\"             % \"1.3.5\",\n      \"io.netty\"               % \"netty-buffer\"            % \"4.1.70.Final\",\n      \"org.apache.commons\"     % \"commons-dbcp2\"           % \"2.9.0\",\n      // test dependencies\n      \"org.mariadb.jdbc\"    % \"mariadb-java-client\" % \"2.+\"    % Test,\n      \"org.scalatest\"       %% \"scalatest\"          % \"3.1.0\"  % Test,\n      \"org.scalacheck\"      %% \"scalacheck\"         % \"1.14.1\" % Test,\n      \"org.mockito\"         %% \"mockito-scala\"      % \"2.0.0\"  % Test,\n      \"com.github.mrpowers\" %% \"spark-fast-tests\"   % \"1.1.0\"  % Test,\n      \"com.github.mrpowers\" %% \"spark-daria\"        % \"1.2.3\"  % Test\n    ),\n    dependencyOverrides += \"com.fasterxml.jackson.core\" % \"jackson-databind\" % jacksonDatabindVersion,\n    Test / testOptions += Tests.Argument(\"-oF\"),\n    Test / fork := true,\n    buildInfoKeys := Seq[BuildInfoKey](version),\n    buildInfoPackage := \"com.singlestore.spark\"\n  )\n\ncredentials += Credentials(\n  \"GnuPG Key ID\",\n  \"gpg\",\n  \"CDD996495CF08BB2041D86D8D1EB3D14F1CD334F\",\n  \"ignored\" // this field is ignored; passwords are supplied by pinentry\n)\n\nassemblyMergeStrategy in assembly := {\n  case PathList(\"META-INF\", _*) => MergeStrategy.discard\n  case _                        => MergeStrategy.first\n}\n\npublishTo := sonatypePublishToBundle.value\npublishMavenStyle := true\nsonatypeSessionName := s\"[sbt-sonatype] ${name.value} ${version.value}\"\nsonatypeProjectHosting := Some(GitHubHosting(\"memsql\", \"memsql-spark-connector\", \"carl@memsql.com\"))\nTest / javaOptions += \"--add-opens=java.base/sun.util.calendar=ALL-UNNAMED\"\nsonatypeCredentialHost := sonatypeCentralHost\n"
  },
  {
    "path": "demo/Dockerfile",
    "content": "FROM apache/zeppelin:0.9.0\n\nENV SPARK_VERSION=4.0.0\n\nUSER root\n\nRUN wget https://apache.ip-connect.vn.ua/spark/spark-${SPARK_VERSION}/spark-${SPARK_VERSION}-bin-hadoop2.7.tgz\nRUN tar xf spark-${SPARK_VERSION}-bin-hadoop2.7.tgz -C /\nRUN rm -rf spark-${SPARK_VERSION}-bin-hadoop2.7.tgz\nENV SPARK_HOME=/spark-${SPARK_VERSION}-bin-hadoop2.7\nENV ZEPPELIN_PORT=8082\nRUN rm -rf /zeppelin/notebook/*\n\nEXPOSE ${ZEPPELIN_PORT}/tcp\n"
  },
  {
    "path": "demo/README.md",
    "content": "## singlestore-spark-connector demo\n\nThis is Dockerfile which uses the upstream [Zeppelin Image](https://hub.docker.com/r/apache/zeppelin/) as it's base\nand has two notebooks with examples of singlestore-spark-connector.\n\nTo run this docker with [MemSQL CIAB](https://hub.docker.com/r/memsql/cluster-in-a-box) follow the instructions\n\n* Create a docker network to be able to connect zeppelin and memsql-ciab\n```\ndocker network create zeppelin-ciab-network\n```\n\n* Pull memsql-ciab docker image\n```\ndocker pull memsql/cluster-in-a-box\n```\n\n* Run and start the SingleStore Cluster in a Box docker container\n\n```\ndocker run -i --init \\\n--name singlestore-ciab-for-zeppelin \\\n-e LICENSE_KEY=[INPUT_YOUR_LICENSE_KEY] \\\n-e ROOT_PASSWORD=my_password \\\n-p 3306:3306 -p 8081:8080 \\\n--net=zeppelin-ciab-network \\\nmemsql/cluster-in-a-box\n```\n```\ndocker start singlestore-ciab-for-zeppelin\n```\n> :note: in this step you can hit a port collision error\n>\n> ```\n> docker: Error response from daemon: driver failed programming external connectivity on endpoint singlestore-ciab-for-zeppelin\n> (38b0df3496f1ec83f120242a53a7023d8a0b74db67f5e487fb23641983c67a76):\n> Bind for 0.0.0.0:8080 failed: port is already allocated.\n> ERRO[0000] error waiting for container: context canceled\n> ```\n>\n> If it happened then remove the container\n>\n>`docker rm singlestore-ciab-for-zeppelin`\n>\n> and run the first command with other ports `-p {new_port1}:3306 -p {new_port2}:8080`\n\n* Build zeppelin docker image in `singlestore-spark-connector/demo` folder\n\n```\ndocker build -t zeppelin .\n```\n\n* Run zeppelin docker container\n```\ndocker run -d --init \\\n--name zeppelin \\\n-p 8082:8082 \\\n--net=zeppelin-ciab-network \\\n-v $PWD/notebook:/opt/zeppelin/notebook/singlestore \\\n-v $PWD/notebook:/zeppelin/notebook/singlestore \\\nzeppelin\n```\n\n> :note: in this step you can hit a port collision error\n>\n> ```\n> docker: Error response from daemon: driver failed programming external connectivity on endpoint zeppelin\n> (38b0df3496f1ec83f120242a53a7023d8a0b74db67f5e487fb23641983c67a76):\n> Bind for 0.0.0.0:8082 failed: port is already allocated.\n> ERRO[0000] error waiting for container: context canceled\n> ```\n>\n> If it happened then remove the container\n>\n>`docker rm zeppelin`\n>\n> and run this command with other port `-p {new_port}:8082`\n\n\n* open [zeppelin](http://localhost:8082/next) in your browser and try\n[scala](http://localhost:8082/next/#/notebook/2F8XQUKFG),\n[pyspark](http://localhost:8082/next/#/notebook/2F6Y3APTX)\nand [spark sql](http://localhost:8082/next/#/notebook/2F7PZ81H6) notebooks\n\nFor setting up more powerful SingleStore trial cluster use [SingleStore Managed Service](https://www.singlestore.com/managed-service/)\n"
  },
  {
    "path": "demo/notebook/pyspark-singlestore-demo_2F8XQUKFG.zpln",
    "content": "{\n  \"paragraphs\": [\n    {\n      \"text\": \"%md\\n## This is a small demo that illustrates the usage of the SingleStore-Spark connector. \\n#### It connects to the ciab docker container (https://hub.docker.com/r/singlestore/cluster-in-a-box) and runs some basic queries on it.\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2021-09-23 10:50:07.311\",\n      \"progress\": 0,\n      \"config\": {\n        \"editorSetting\": {\n          \"language\": \"markdown\",\n          \"editOnDblClick\": true,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": false\n        },\n        \"colWidth\": 12.0,\n        \"editorMode\": \"ace/mode/markdown\",\n        \"fontSize\": 15.0,\n        \"editorHide\": true,\n        \"results\": {},\n        \"enabled\": true,\n        \"tableHide\": false\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"HTML\",\n            \"data\": \"\\u003cdiv class\\u003d\\\"markdown-body\\\"\\u003e\\n\\u003ch2\\u003eThis is a small demo that illustrates the usage of the SingleStore-Spark connector.\\u003c/h2\\u003e\\n\\u003ch4\\u003eIt connects to the ciab docker container (\\u003ca href\\u003d\\\"https://hub.docker.com/r/singlestore/cluster-in-a-box\\\"\\u003ehttps://hub.docker.com/r/singlestore/cluster-in-a-box\\u003c/a\\u003e) and runs some basic queries on it.\\u003c/h4\\u003e\\n\\n\\u003c/div\\u003e\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553845758_1676143899\",\n      \"id\": \"paragraph_1587550420891_1388924274\",\n      \"dateCreated\": \"2020-04-22 11:10:45.758\",\n      \"dateStarted\": \"2021-09-23 10:50:07.317\",\n      \"dateFinished\": \"2021-09-23 10:50:07.331\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Configure Spark\",\n      \"text\": \"%spark.conf\\n\\n// Comma-separated list of Maven coordinates of jars to include on the driver and executor classpaths\\nspark.jars.packages com.singlestore:singlestore-spark-connector_2.12:5.0.1-spark-4.0.0\\n\\n// The hostname or IP address of the SingleStore Master Aggregator in the `host[:port]` format, where port is an optional parameter\\n// singlestore-ciab-for-zeppelin - hostname of the docker created by https://hub.docker.com/r/singlestore/cluster-in-a-box\\n// 3306 - port on which SingleStore Master Aggregator is started\\nspark.datasource.singlestore.ddlEndpoint singlestore-ciab-for-zeppelin:3306\\n\\n// The hostname or IP address of SingleStore Aggregator nodes to run queries against in the `host[:port],host[:port],...` format, \\n// where :port is an optional parameter (multiple hosts separated by comma) (default: ddlEndpoint)\\n// Example\\n// spark.datasource.singlestore.dmlEndpoints child-agg:3308,child-agg2\\nspark.datasource.singlestore.dmlEndpoints singlestore-ciab-for-zeppelin:3306\\n\\n// SingleStore username (default: root)\\nspark.datasource.singlestore.user root\\n\\n// SingleStore password (default: no password)\\nspark.datasource.singlestore.password my_password\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:26:15.232\",\n      \"progress\": 0,\n      \"config\": {\n        \"lineNumbers\": false,\n        \"tableHide\": false,\n        \"editorSetting\": {\n          \"language\": \"text\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"colWidth\": 12.0,\n        \"editorMode\": \"ace/mode/text\",\n        \"fontSize\": 13.0,\n        \"title\": true,\n        \"results\": {},\n        \"enabled\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": []\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553845761_760134801\",\n      \"id\": \"paragraph_1587546884632_-2089202077\",\n      \"dateCreated\": \"2020-04-22 11:10:45.761\",\n      \"dateStarted\": \"2022-07-06 11:26:15.237\",\n      \"dateFinished\": \"2022-07-06 11:26:15.245\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Create a database using SQLHelpers\",\n      \"text\": \"import com.singlestore.spark.SQLHelper.QueryMethods\\n\\nspark.executeSinglestoreQuery(\\\"create database if not exists demoDB\\\")\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:32:33.191\",\n      \"progress\": 0,\n      \"config\": {\n        \"lineNumbers\": true,\n        \"tableHide\": false,\n        \"editorSetting\": {},\n        \"colWidth\": 12.0,\n        \"fontSize\": 13.0,\n        \"editorHide\": false,\n        \"title\": true,\n        \"results\": {},\n        \"enabled\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"import com.singlestore.spark.SQLHelper.QueryMethods\\n\\u001b[1m\\u001b[34mres1\\u001b[0m: \\u001b[1m\\u001b[32mIterator[org.apache.spark.sql.Row]\\u001b[0m \\u003d \\u003citerator\\u003e\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1657106390307_996443657\",\n      \"id\": \"paragraph_1657106390307_996443657\",\n      \"dateCreated\": \"2022-07-06 11:19:50.307\",\n      \"dateStarted\": \"2022-07-06 11:26:22.057\",\n      \"dateFinished\": \"2022-07-06 11:27:04.138\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Writing to SingleStore\",\n      \"text\": \"%spark.pyspark\\n\\npeople1 \\u003d spark.createDataFrame([\\n    (1, \\\"andy\\\", 5, \\\"USA\\\"), \\n    (2, \\\"jeff\\\", 23, \\\"China\\\"), \\n    (3, \\\"james\\\", 62, \\\"USA\\\")\\n    ]).toDF(\\\"id\\\", \\\"name\\\", \\\"age\\\", \\\"country\\\")\\npeople1.printSchema\\npeople1.show()\\n\\npeople1.write \\\\\\n    .format(\\\"singlestore\\\") \\\\\\n    .mode(\\\"overwrite\\\") \\\\\\n    .save(\\\"demoDB.people\\\") # write to table `people` in database `demoDB`\\n    \\npeople2 \\u003d people1.withColumn(\\\"age2\\\", people1[\\\"age\\\"] + 1)\\npeople1.printSchema\\npeople2.show()\\n\\npeople2.write \\\\\\n    .format(\\\"singlestore\\\") \\\\\\n    .option(\\\"loadDataCompression\\\", \\\"LZ4\\\") \\\\\\n    .mode(\\\"overwrite\\\") \\\\\\n    .save(\\\"demoDB.people\\\") # write to table `people` in database `demoDB` \",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:27:10.614\",\n      \"progress\": 0,\n      \"config\": {\n        \"lineNumbers\": true,\n        \"tableHide\": false,\n        \"editorSetting\": {\n          \"language\": \"python\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"colWidth\": 6.0,\n        \"editorMode\": \"ace/mode/python\",\n        \"fontSize\": 13.0,\n        \"editorHide\": false,\n        \"title\": true,\n        \"results\": {},\n        \"enabled\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"+---+-----+---+-------+\\n| id| name|age|country|\\n+---+-----+---+-------+\\n|  1| andy|  5|    USA|\\n|  2| jeff| 23|  China|\\n|  3|james| 62|    USA|\\n+---+-----+---+-------+\\n\\n+---+-----+---+-------+----+\\n| id| name|age|country|age2|\\n+---+-----+---+-------+----+\\n|  1| andy|  5|    USA|   6|\\n|  2| jeff| 23|  China|  24|\\n|  3|james| 62|    USA|  63|\\n+---+-----+---+-------+----+\\n\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {\n        \"jobUrl\": {\n          \"propertyName\": \"jobUrl\",\n          \"label\": \"SPARK JOB\",\n          \"tooltip\": \"View in Spark web UI\",\n          \"group\": \"spark\",\n          \"values\": [\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d0\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d1\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d2\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d3\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d4\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d5\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d6\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d7\"\n            }\n          ],\n          \"interpreterSettingId\": \"spark\"\n        }\n      },\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553845761_-1027258033\",\n      \"id\": \"paragraph_1587547555609_-348809680\",\n      \"dateCreated\": \"2020-04-22 11:10:45.761\",\n      \"dateStarted\": \"2022-07-06 11:27:10.622\",\n      \"dateFinished\": \"2022-07-06 11:27:22.543\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Reading from SingleStore\",\n      \"text\": \"%spark.pyspark\\n\\npeople \\u003d spark.read \\\\\\n    .format(\\\"singlestore\\\") \\\\\\n    .load(\\\"demoDB.people\\\")\\npeople.printSchema\\npeople.show()\\n\\nchildren \\u003d spark.read \\\\\\n    .format(\\\"singlestore\\\") \\\\\\n    .load(\\\"demoDB.people\\\") \\\\\\n    .filter(\\\"age \\u003c 10\\\")\\npeople.printSchema\\nchildren.show()\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:27:30.331\",\n      \"progress\": 0,\n      \"config\": {\n        \"tableHide\": false,\n        \"editorSetting\": {\n          \"language\": \"python\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"colWidth\": 6.0,\n        \"editorMode\": \"ace/mode/python\",\n        \"fontSize\": 13.0,\n        \"title\": true,\n        \"results\": {},\n        \"enabled\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"+---+-----+---+-------+----+\\n| id| name|age|country|age2|\\n+---+-----+---+-------+----+\\n|  3|james| 62|    USA|  63|\\n|  1| andy|  5|    USA|   6|\\n|  2| jeff| 23|  China|  24|\\n+---+-----+---+-------+----+\\n\\n+---+----+---+-------+----+\\n| id|name|age|country|age2|\\n+---+----+---+-------+----+\\n|  1|andy|  5|    USA|   6|\\n+---+----+---+-------+----+\\n\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {\n        \"jobUrl\": {\n          \"propertyName\": \"jobUrl\",\n          \"label\": \"SPARK JOB\",\n          \"tooltip\": \"View in Spark web UI\",\n          \"group\": \"spark\",\n          \"values\": [\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d8\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d9\"\n            }\n          ],\n          \"interpreterSettingId\": \"spark\"\n        }\n      },\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553845762_-1342936354\",\n      \"id\": \"paragraph_1587548897148_-478225566\",\n      \"dateCreated\": \"2020-04-22 11:10:45.762\",\n      \"dateStarted\": \"2022-07-06 11:27:30.333\",\n      \"dateFinished\": \"2022-07-06 11:27:33.067\",\n      \"status\": \"FINISHED\"\n    }\n  ],\n  \"name\": \"pyspark-singlestore-demo\",\n  \"id\": \"2F8XQUKFG\",\n  \"defaultInterpreterGroup\": \"spark\",\n  \"version\": \"0.9.0-preview1\",\n  \"noteParams\": {},\n  \"noteForms\": {},\n  \"angularObjects\": {},\n  \"config\": {\n    \"isZeppelinNotebookCronEnable\": false\n  },\n  \"info\": {}\n}\n"
  },
  {
    "path": "demo/notebook/scala-singlestore-demo_2F6Y3APTX.zpln",
    "content": "{\n  \"paragraphs\": [\n    {\n      \"text\": \"%md\\n## This is a small demo that illustrates the usage of the SingleStore-Spark connector. \\n#### It connects to the ciab docker container (https://hub.docker.com/r/singlestore/cluster-in-a-box) and runs some basic queries on it.\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2021-09-23 10:55:20.428\",\n      \"progress\": 0,\n      \"config\": {\n        \"editorSetting\": {\n          \"language\": \"markdown\",\n          \"editOnDblClick\": true,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": false\n        },\n        \"colWidth\": 12.0,\n        \"editorMode\": \"ace/mode/markdown\",\n        \"fontSize\": 15.0,\n        \"results\": {},\n        \"enabled\": true,\n        \"editorHide\": true,\n        \"tableHide\": false\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"HTML\",\n            \"data\": \"\\u003cdiv class\\u003d\\\"markdown-body\\\"\\u003e\\n\\u003ch2\\u003eThis is a small demo that illustrates the usage of the SingleStore-Spark connector.\\u003c/h2\\u003e\\n\\u003ch4\\u003eIt connects to the ciab docker container (\\u003ca href\\u003d\\\"https://hub.docker.com/r/singlestore/cluster-in-a-box\\\"\\u003ehttps://hub.docker.com/r/singlestore/cluster-in-a-box\\u003c/a\\u003e) and runs some basic queries on it.\\u003c/h4\\u003e\\n\\n\\u003c/div\\u003e\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553015155_-1751456317\",\n      \"id\": \"paragraph_1587550420891_1388924274\",\n      \"dateCreated\": \"2020-04-22 10:56:55.155\",\n      \"dateStarted\": \"2021-09-23 10:55:20.434\",\n      \"dateFinished\": \"2021-09-23 10:55:20.452\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Configure Spark\",\n      \"text\": \"%spark.conf\\n\\n// Comma-separated list of Maven coordinates of jars to include on the driver and executor classpaths\\nspark.jars.packages com.singlestore:singlestore-spark-connector_2.12:5.0.1-spark-4.0.0\\n\\n// The hostname or IP address of the SingleStore Master Aggregator in the `host[:port]` format, where port is an optional parameter\\n// singlestore-ciab-for-zeppelin - hostname of the docker created by https://hub.docker.com/r/singlestore/cluster-in-a-box\\n// 3306 - port on which SingleStore Master Aggregator is started\\nspark.datasource.singlestore.ddlEndpoint singlestore-ciab-for-zeppelin:3306\\n\\n// The hostname or IP address of SingleStore Aggregator nodes to run queries against in the `host[:port],host[:port],...` format, \\n// where :port is an optional parameter (multiple hosts separated by comma) (default: ddlEndpoint)\\n// Example\\n// spark.datasource.singlestore.dmlEndpoints child-agg:3308,child-agg2\\nspark.datasource.singlestore.dmlEndpoints singlestore-ciab-for-zeppelin:3306\\n\\n// SingleStore username (default: root)\\nspark.datasource.singlestore.user root\\n\\n// SingleStore password (default: no password)\\nspark.datasource.singlestore.password my_password\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:31:08.311\",\n      \"progress\": 0,\n      \"config\": {\n        \"lineNumbers\": false,\n        \"tableHide\": false,\n        \"editorSetting\": {\n          \"language\": \"text\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"colWidth\": 12.0,\n        \"editorMode\": \"ace/mode/text\",\n        \"fontSize\": 13.0,\n        \"title\": true,\n        \"results\": {},\n        \"enabled\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": []\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553015155_499760817\",\n      \"id\": \"paragraph_1587546884632_-2089202077\",\n      \"dateCreated\": \"2020-04-22 10:56:55.155\",\n      \"dateStarted\": \"2022-07-06 11:31:08.315\",\n      \"dateFinished\": \"2022-07-06 11:31:08.323\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Create a database using SQLHelpers\",\n      \"text\": \"import com.singlestore.spark.SQLHelper.QueryMethods\\n\\nspark.executeSinglestoreQuery(\\\"create database if not exists demoDB\\\")\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:31:11.146\",\n      \"progress\": 0,\n      \"config\": {\n        \"colWidth\": 12.0,\n        \"fontSize\": 13.0,\n        \"enabled\": true,\n        \"results\": {},\n        \"editorSetting\": {\n          \"language\": \"scala\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"editorMode\": \"ace/mode/scala\",\n        \"title\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"import com.singlestore.spark.SQLHelper.QueryMethods\\n\\u001b[1m\\u001b[34mres2\\u001b[0m: \\u001b[1m\\u001b[32mIterator[org.apache.spark.sql.Row]\\u001b[0m \\u003d \\u003citerator\\u003e\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587581984336_-994182625\",\n      \"id\": \"paragraph_1587581984336_-994182625\",\n      \"dateCreated\": \"2020-04-22 18:59:44.336\",\n      \"dateStarted\": \"2022-07-06 11:31:11.153\",\n      \"dateFinished\": \"2022-07-06 11:31:11.475\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Writing to SingleStore\",\n      \"text\": \"import org.apache.spark.sql.{SaveMode}\\n\\nval people1 \\u003d spark.createDataFrame(Seq(\\n    (1, \\\"andy\\\", 5, \\\"USA\\\"), \\n    (2, \\\"jeff\\\", 23, \\\"China\\\"), \\n    (3, \\\"james\\\", 62, \\\"USA\\\")\\n    )).toDF(\\\"id\\\", \\\"name\\\", \\\"age\\\", \\\"country\\\")\\npeople1.show()\\n\\npeople1.write\\n    .format(\\\"singlestore\\\")\\n    .mode(SaveMode.Overwrite) // recreate table if it exists\\n    .save(\\\"demoDB.people\\\") // write to table `people` in database `demoDB`\\n    \\nval people2 \\u003d people1.withColumn(\\\"age2\\\", $\\\"age\\\" + 1)\\npeople2.show()\\n\\npeople2.write\\n    .format(\\\"singlestore\\\")\\n    .option(\\\"loadDataCompression\\\", \\\"LZ4\\\") // compress data on load with LZ4\\n    .mode(SaveMode.Overwrite) // recreate table if it exists\\n    .save(\\\"demoDB.people\\\") // write to table `people` in database `demoDB` \",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:31:13.348\",\n      \"progress\": 100,\n      \"config\": {\n        \"lineNumbers\": true,\n        \"tableHide\": false,\n        \"editorSetting\": {\n          \"language\": \"scala\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"colWidth\": 6.0,\n        \"editorMode\": \"ace/mode/scala\",\n        \"fontSize\": 13.0,\n        \"editorHide\": false,\n        \"title\": true,\n        \"results\": {},\n        \"enabled\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"+---+-----+---+-------+\\n| id| name|age|country|\\n+---+-----+---+-------+\\n|  1| andy|  5|    USA|\\n|  2| jeff| 23|  China|\\n|  3|james| 62|    USA|\\n+---+-----+---+-------+\\n\\n+---+-----+---+-------+----+\\n| id| name|age|country|age2|\\n+---+-----+---+-------+----+\\n|  1| andy|  5|    USA|   6|\\n|  2| jeff| 23|  China|  24|\\n|  3|james| 62|    USA|  63|\\n+---+-----+---+-------+----+\\n\\nimport org.apache.spark.sql.SaveMode\\n\\u001b[1m\\u001b[34mpeople1\\u001b[0m: \\u001b[1m\\u001b[32morg.apache.spark.sql.DataFrame\\u001b[0m \\u003d [id: int, name: string ... 2 more fields]\\n\\u001b[1m\\u001b[34mpeople2\\u001b[0m: \\u001b[1m\\u001b[32morg.apache.spark.sql.DataFrame\\u001b[0m \\u003d [id: int, name: string ... 3 more fields]\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {\n        \"jobUrl\": {\n          \"propertyName\": \"jobUrl\",\n          \"label\": \"SPARK JOB\",\n          \"tooltip\": \"View in Spark web UI\",\n          \"group\": \"spark\",\n          \"values\": [\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d10\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d11\"\n            }\n          ],\n          \"interpreterSettingId\": \"spark\"\n        }\n      },\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553015156_498470796\",\n      \"id\": \"paragraph_1587547555609_-348809680\",\n      \"dateCreated\": \"2020-04-22 10:56:55.156\",\n      \"dateStarted\": \"2022-07-06 11:31:13.357\",\n      \"dateFinished\": \"2022-07-06 11:31:15.846\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Reading from SingleStore\",\n      \"text\": \"val people \\u003d spark.read\\n    .format(\\\"singlestore\\\")\\n    .load(\\\"demoDB.people\\\")\\npeople.show()\\n\\nval children \\u003d spark.read\\n    .format(\\\"singlestore\\\")\\n    .load(\\\"demoDB.people\\\")\\n    .filter($\\\"age\\\" \\u003c 10)\\nchildren.show()\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:31:17.899\",\n      \"progress\": 6,\n      \"config\": {\n        \"tableHide\": false,\n        \"editorSetting\": {\n          \"language\": \"scala\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"colWidth\": 6.0,\n        \"editorMode\": \"ace/mode/scala\",\n        \"fontSize\": 13.0,\n        \"title\": true,\n        \"results\": {},\n        \"enabled\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"+---+-----+---+-------+----+\\n| id| name|age|country|age2|\\n+---+-----+---+-------+----+\\n|  2| jeff| 23|  China|  24|\\n|  3|james| 62|    USA|  63|\\n|  1| andy|  5|    USA|   6|\\n+---+-----+---+-------+----+\\n\\n+---+----+---+-------+----+\\n| id|name|age|country|age2|\\n+---+----+---+-------+----+\\n|  1|andy|  5|    USA|   6|\\n+---+----+---+-------+----+\\n\\n\\u001b[1m\\u001b[34mpeople\\u001b[0m: \\u001b[1m\\u001b[32morg.apache.spark.sql.DataFrame\\u001b[0m \\u003d [id: int, name: string ... 3 more fields]\\n\\u001b[1m\\u001b[34mchildren\\u001b[0m: \\u001b[1m\\u001b[32morg.apache.spark.sql.Dataset[org.apache.spark.sql.Row]\\u001b[0m \\u003d [id: int, name: string ... 3 more fields]\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {\n        \"jobUrl\": {\n          \"propertyName\": \"jobUrl\",\n          \"label\": \"SPARK JOB\",\n          \"tooltip\": \"View in Spark web UI\",\n          \"group\": \"spark\",\n          \"values\": [\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d12\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d13\"\n            }\n          ],\n          \"interpreterSettingId\": \"spark\"\n        }\n      },\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587553015156_-836094162\",\n      \"id\": \"paragraph_1587548897148_-478225566\",\n      \"dateCreated\": \"2020-04-22 10:56:55.156\",\n      \"dateStarted\": \"2022-07-06 11:31:17.906\",\n      \"dateFinished\": \"2022-07-06 11:31:20.525\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"text\": \"\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:31:28.014\",\n      \"progress\": 0,\n      \"config\": {\n        \"colWidth\": 12.0,\n        \"fontSize\": 9.0,\n        \"results\": {},\n        \"enabled\": true,\n        \"editorSetting\": {}\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1657107077904_308239458\",\n      \"id\": \"paragraph_1657107077904_308239458\",\n      \"dateCreated\": \"2022-07-06 11:31:17.904\",\n      \"status\": \"READY\"\n    }\n  ],\n  \"name\": \"scala-singlestore-demo\",\n  \"id\": \"2F6Y3APTX\",\n  \"defaultInterpreterGroup\": \"spark\",\n  \"version\": \"0.9.0-preview1\",\n  \"noteParams\": {},\n  \"noteForms\": {},\n  \"angularObjects\": {},\n  \"config\": {\n    \"isZeppelinNotebookCronEnable\": false\n  },\n  \"info\": {}\n}\n"
  },
  {
    "path": "demo/notebook/spark-sql-singlestore-demo_2F7PZ81H6.zpln",
    "content": "{\n  \"paragraphs\": [\n    {\n      \"text\": \"%md\\n## This is a small demo that illustrates the usage of the SingleStore-Spark connector. \\n#### It connects to the ciab docker container (https://hub.docker.com/r/singlestore/cluster-in-a-box) and runs some basic queries on it.\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2021-09-23 11:00:50.089\",\n      \"progress\": 0,\n      \"config\": {\n        \"colWidth\": 12.0,\n        \"fontSize\": 13.0,\n        \"enabled\": true,\n        \"results\": {},\n        \"editorSetting\": {\n          \"language\": \"markdown\",\n          \"editOnDblClick\": true,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": false\n        },\n        \"editorMode\": \"ace/mode/markdown\",\n        \"editorHide\": true,\n        \"tableHide\": false\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"HTML\",\n            \"data\": \"\\u003cdiv class\\u003d\\\"markdown-body\\\"\\u003e\\n\\u003ch2\\u003eThis is a small demo that illustrates the usage of the SingleStore-Spark connector.\\u003c/h2\\u003e\\n\\u003ch4\\u003eIt connects to the ciab docker container (\\u003ca href\\u003d\\\"https://hub.docker.com/r/singlestore/cluster-in-a-box\\\"\\u003ehttps://hub.docker.com/r/singlestore/cluster-in-a-box\\u003c/a\\u003e) and runs some basic queries on it.\\u003c/h4\\u003e\\n\\n\\u003c/div\\u003e\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587645478876_994795668\",\n      \"id\": \"paragraph_1587645478876_994795668\",\n      \"dateCreated\": \"2020-04-23 12:37:58.876\",\n      \"dateStarted\": \"2021-09-23 11:00:50.095\",\n      \"dateFinished\": \"2021-09-23 11:00:50.108\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Configure Spark\",\n      \"text\": \"%spark.conf\\n\\n// Comma-separated list of Maven coordinates of jars to include on the driver and executor classpaths\\nspark.jars.packages com.singlestore:singlestore-spark-connector_2.12:5.0.1-spark-4.0.0\\n\\n// The hostname or IP address of the SingleStore Master Aggregator in the `host[:port]` format, where port is an optional parameter\\n// singlestore-ciab-for-zeppelin - hostname of the docker created by https://hub.docker.com/r/singlestore/cluster-in-a-box\\n// 3306 - port on which SingleStore Master Aggregator is started\\nspark.datasource.singlestore.ddlEndpoint singlestore-ciab-for-zeppelin:3306\\n\\n// The hostname or IP address of SingleStore Aggregator nodes to run queries against in the `host[:port],host[:port],...` format, \\n// where :port is an optional parameter (multiple hosts separated by comma) (default: ddlEndpoint)\\n// Example\\n// spark.datasource.singlestore.dmlEndpoints child-agg:3308,child-agg2\\nspark.datasource.singlestore.dmlEndpoints singlestore-ciab-for-zeppelin:3306\\n\\n// SingleStore username (default: root)\\nspark.datasource.singlestore.user root\\n\\n// SingleStore password (default: no password)\\nspark.datasource.singlestore.password my_password\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:32:22.885\",\n      \"progress\": 0,\n      \"config\": {\n        \"colWidth\": 12.0,\n        \"fontSize\": 13.0,\n        \"enabled\": true,\n        \"results\": {},\n        \"editorSetting\": {\n          \"language\": \"text\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"editorMode\": \"ace/mode/text\",\n        \"title\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": []\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587645537976_1278581633\",\n      \"id\": \"paragraph_1587645537976_1278581633\",\n      \"dateCreated\": \"2020-04-23 12:38:57.976\",\n      \"dateStarted\": \"2022-07-06 11:32:22.890\",\n      \"dateFinished\": \"2022-07-06 11:32:22.898\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Create a database using SQLHelpers\",\n      \"text\": \"import com.singlestore.spark.SQLHelper.QueryMethods\\n\\nspark.executeSinglestoreQuery(\\\"create database if not exists demoDB\\\")\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:32:43.948\",\n      \"progress\": 0,\n      \"config\": {\n        \"colWidth\": 6.0,\n        \"fontSize\": 13.0,\n        \"enabled\": true,\n        \"results\": {},\n        \"editorSetting\": {\n          \"language\": \"scala\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"editorMode\": \"ace/mode/scala\",\n        \"title\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"import com.singlestore.spark.SQLHelper.QueryMethods\\n\\u001b[1m\\u001b[34mres5\\u001b[0m: \\u001b[1m\\u001b[32mIterator[org.apache.spark.sql.Row]\\u001b[0m \\u003d \\u003citerator\\u003e\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587645590204_-1422944272\",\n      \"id\": \"paragraph_1587645590204_-1422944272\",\n      \"dateCreated\": \"2020-04-23 12:39:50.204\",\n      \"dateStarted\": \"2022-07-06 11:32:43.951\",\n      \"dateFinished\": \"2022-07-06 11:32:44.203\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Insert data to the table\",\n      \"text\": \"import org.apache.spark.sql.{SaveMode}\\n\\nval people1 \\u003d spark.createDataFrame(Seq(\\n    (1, \\\"andy\\\", 5, \\\"USA\\\"), \\n    (2, \\\"jeff\\\", 23, \\\"China\\\"), \\n    (3, \\\"james\\\", 62, \\\"USA\\\"),\\n    (4, \\\"clara\\\", 82, \\\"USA\\\"),\\n    (5, \\\"emmy\\\", 9, \\\"Canada\\\")\\n    )).toDF(\\\"id\\\", \\\"name\\\", \\\"age\\\", \\\"country\\\")\\npeople1.show()\\n\\npeople1.write\\n    .format(\\\"singlestore\\\")\\n    .mode(SaveMode.Overwrite) // recreate table if it exists\\n    .save(\\\"demoDB.people\\\") // write to table `people` in database `demoDB`\\n\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:32:46.800\",\n      \"progress\": 0,\n      \"config\": {\n        \"colWidth\": 6.0,\n        \"fontSize\": 13.0,\n        \"enabled\": true,\n        \"results\": {},\n        \"editorSetting\": {\n          \"language\": \"scala\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"editorMode\": \"ace/mode/scala\",\n        \"title\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TEXT\",\n            \"data\": \"+---+-----+---+-------+\\n| id| name|age|country|\\n+---+-----+---+-------+\\n|  1| andy|  5|    USA|\\n|  2| jeff| 23|  China|\\n|  3|james| 62|    USA|\\n|  4|clara| 82|    USA|\\n|  5| emmy|  9| Canada|\\n+---+-----+---+-------+\\n\\nimport org.apache.spark.sql.SaveMode\\n\\u001b[1m\\u001b[34mpeople1\\u001b[0m: \\u001b[1m\\u001b[32morg.apache.spark.sql.DataFrame\\u001b[0m \\u003d [id: int, name: string ... 2 more fields]\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {\n        \"jobUrl\": {\n          \"propertyName\": \"jobUrl\",\n          \"label\": \"SPARK JOB\",\n          \"tooltip\": \"View in Spark web UI\",\n          \"group\": \"spark\",\n          \"values\": [\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d14\"\n            }\n          ],\n          \"interpreterSettingId\": \"spark\"\n        }\n      },\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587646718045_-758896661\",\n      \"id\": \"paragraph_1587646718045_-758896661\",\n      \"dateCreated\": \"2020-04-23 12:58:38.045\",\n      \"dateStarted\": \"2022-07-06 11:32:46.807\",\n      \"dateFinished\": \"2022-07-06 11:32:47.382\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Create a Spark SQL table backed by a SingleStore table\",\n      \"text\": \"%sql\\nCREATE DATABASE IF NOT EXISTS demoDB;\\nCREATE TABLE IF NOT EXISTS demoDB.people USING singlestore OPTIONS (\\u0027dbtable\\u0027\\u003d\\u0027demoDB.people\\u0027);\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:33:29.031\",\n      \"progress\": 0,\n      \"config\": {\n        \"colWidth\": 6.0,\n        \"fontSize\": 13.0,\n        \"enabled\": true,\n        \"results\": {},\n        \"editorSetting\": {\n          \"language\": \"sql\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"editorMode\": \"ace/mode/sql\",\n        \"title\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": []\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587645780167_-228404075\",\n      \"id\": \"paragraph_1587645780167_-228404075\",\n      \"dateCreated\": \"2020-04-23 12:43:00.167\",\n      \"dateStarted\": \"2022-07-06 11:33:29.037\",\n      \"dateFinished\": \"2022-07-06 11:33:29.285\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"title\": \"Run a raw Spark SQL query over a Spark SQL table\",\n      \"text\": \"%sql\\nSELECT * FROM demoDB.people;\\nSELECT * FROM demoDB.people WHERE people.age \\u003c 10;\\nSELECT country, MAX(age) FROM demoDB.people GROUP BY country;\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:33:31.802\",\n      \"progress\": 56,\n      \"config\": {\n        \"colWidth\": 6.0,\n        \"fontSize\": 13.0,\n        \"enabled\": true,\n        \"results\": {\n          \"0\": {\n            \"graph\": {\n              \"mode\": \"table\",\n              \"height\": 300.0,\n              \"optionOpen\": false,\n              \"setting\": {\n                \"table\": {\n                  \"tableGridState\": {},\n                  \"tableColumnTypeState\": {\n                    \"names\": {\n                      \"id\": \"string\",\n                      \"name\": \"string\",\n                      \"age\": \"string\",\n                      \"country\": \"string\"\n                    },\n                    \"updated\": false\n                  },\n                  \"tableOptionSpecHash\": \"[{\\\"name\\\":\\\"useFilter\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable filter for columns\\\"},{\\\"name\\\":\\\"showPagination\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable pagination for better navigation\\\"},{\\\"name\\\":\\\"showAggregationFooter\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable a footer for displaying aggregated values\\\"}]\",\n                  \"tableOptionValue\": {\n                    \"useFilter\": false,\n                    \"showPagination\": false,\n                    \"showAggregationFooter\": false\n                  },\n                  \"updated\": false,\n                  \"initialized\": false\n                }\n              },\n              \"commonSetting\": {}\n            }\n          },\n          \"1\": {\n            \"graph\": {\n              \"mode\": \"table\",\n              \"height\": 300.0,\n              \"optionOpen\": false,\n              \"setting\": {\n                \"table\": {\n                  \"tableGridState\": {},\n                  \"tableColumnTypeState\": {\n                    \"names\": {\n                      \"id\": \"string\",\n                      \"name\": \"string\",\n                      \"age\": \"string\",\n                      \"country\": \"string\"\n                    },\n                    \"updated\": false\n                  },\n                  \"tableOptionSpecHash\": \"[{\\\"name\\\":\\\"useFilter\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable filter for columns\\\"},{\\\"name\\\":\\\"showPagination\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable pagination for better navigation\\\"},{\\\"name\\\":\\\"showAggregationFooter\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable a footer for displaying aggregated values\\\"}]\",\n                  \"tableOptionValue\": {\n                    \"useFilter\": false,\n                    \"showPagination\": false,\n                    \"showAggregationFooter\": false\n                  },\n                  \"updated\": false,\n                  \"initialized\": false\n                }\n              },\n              \"commonSetting\": {}\n            }\n          },\n          \"2\": {\n            \"graph\": {\n              \"mode\": \"table\",\n              \"height\": 300.0,\n              \"optionOpen\": false,\n              \"setting\": {\n                \"table\": {\n                  \"tableGridState\": {},\n                  \"tableColumnTypeState\": {\n                    \"names\": {\n                      \"country\": \"string\",\n                      \"max(age)\": \"string\"\n                    },\n                    \"updated\": false\n                  },\n                  \"tableOptionSpecHash\": \"[{\\\"name\\\":\\\"useFilter\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable filter for columns\\\"},{\\\"name\\\":\\\"showPagination\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable pagination for better navigation\\\"},{\\\"name\\\":\\\"showAggregationFooter\\\",\\\"valueType\\\":\\\"boolean\\\",\\\"defaultValue\\\":false,\\\"widget\\\":\\\"checkbox\\\",\\\"description\\\":\\\"Enable a footer for displaying aggregated values\\\"}]\",\n                  \"tableOptionValue\": {\n                    \"useFilter\": false,\n                    \"showPagination\": false,\n                    \"showAggregationFooter\": false\n                  },\n                  \"updated\": false,\n                  \"initialized\": false\n                }\n              },\n              \"commonSetting\": {}\n            }\n          }\n        },\n        \"editorSetting\": {\n          \"language\": \"sql\",\n          \"editOnDblClick\": false,\n          \"completionKey\": \"TAB\",\n          \"completionSupport\": true\n        },\n        \"editorMode\": \"ace/mode/sql\",\n        \"title\": true\n      },\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"results\": {\n        \"code\": \"SUCCESS\",\n        \"msg\": [\n          {\n            \"type\": \"TABLE\",\n            \"data\": \"id\\tname\\tage\\tcountry\\n2\\tjeff\\t23\\tChina\\n4\\tclara\\t82\\tUSA\\n3\\tjames\\t62\\tUSA\\n5\\temmy\\t9\\tCanada\\n1\\tandy\\t5\\tUSA\\n\"\n          },\n          {\n            \"type\": \"TABLE\",\n            \"data\": \"id\\tname\\tage\\tcountry\\n5\\temmy\\t9\\tCanada\\n1\\tandy\\t5\\tUSA\\n\"\n          },\n          {\n            \"type\": \"TABLE\",\n            \"data\": \"country\\tmax(age)\\nChina\\t23\\nCanada\\t9\\nUSA\\t82\\n\"\n          }\n        ]\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {\n        \"jobUrl\": {\n          \"propertyName\": \"jobUrl\",\n          \"label\": \"SPARK JOB\",\n          \"tooltip\": \"View in Spark web UI\",\n          \"group\": \"spark\",\n          \"values\": [\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d15\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d16\"\n            },\n            {\n              \"jobUrl\": \"http://322bfd970e79:4040/jobs/job?id\\u003d17\"\n            }\n          ],\n          \"interpreterSettingId\": \"spark\"\n        }\n      },\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1587646114445_960936248\",\n      \"id\": \"paragraph_1587646114445_960936248\",\n      \"dateCreated\": \"2020-04-23 12:48:34.446\",\n      \"dateStarted\": \"2022-07-06 11:33:31.809\",\n      \"dateFinished\": \"2022-07-06 11:33:34.428\",\n      \"status\": \"FINISHED\"\n    },\n    {\n      \"text\": \"%sql\\n\",\n      \"user\": \"anonymous\",\n      \"dateUpdated\": \"2022-07-06 11:33:31.807\",\n      \"progress\": 0,\n      \"config\": {},\n      \"settings\": {\n        \"params\": {},\n        \"forms\": {}\n      },\n      \"apps\": [],\n      \"runtimeInfos\": {},\n      \"progressUpdateIntervalMs\": 500,\n      \"jobName\": \"paragraph_1657107211807_874039426\",\n      \"id\": \"paragraph_1657107211807_874039426\",\n      \"dateCreated\": \"2022-07-06 11:33:31.807\",\n      \"status\": \"READY\"\n    }\n  ],\n  \"name\": \"spark-sql-singlestore-demo\",\n  \"id\": \"2F7PZ81H6\",\n  \"defaultInterpreterGroup\": \"spark\",\n  \"version\": \"0.9.0-preview1\",\n  \"noteParams\": {},\n  \"noteForms\": {},\n  \"angularObjects\": {},\n  \"config\": {\n    \"isZeppelinNotebookCronEnable\": false\n  },\n  \"info\": {}\n}\n"
  },
  {
    "path": "project/build.properties",
    "content": "sbt.version=1.3.8\n"
  },
  {
    "path": "project/plugins.sbt",
    "content": "addSbtPlugin(\"org.xerial.sbt\" % \"sbt-sonatype\"  % \"3.12.2\")\naddSbtPlugin(\"com.jsuereth\"   % \"sbt-pgp\"       % \"2.0.1\")\naddSbtPlugin(\"com.eed3si9n\"   % \"sbt-buildinfo\" % \"0.11.0\")\naddSbtPlugin(\"com.eed3si9n\"   % \"sbt-assembly\"  % \"2.2.0\")\n"
  },
  {
    "path": "scripts/jwt/jwt_auth_config.json",
    "content": "{\n  \"username_claim\": \"username\",\n  \"methods\": [\n      {\n          \"algorithms\": [ \"RS384\" ],\n          \"secret\": \"-----BEGIN PUBLIC KEY-----\\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0i0dDauX6iaOaocic99O\\nUTruTYPWFUv50aHTgfKxenFKpJTTL43T8ON36whwyObM3r/ayhPoyPxvSfkkCwxd\\nE7XEmTRARHkJKQfebkbN6SaKlEgIdmZ8UroZCslSzOcsX0N1KNc3WSyFeOigHSp/\\nww+roVtaJC/OJQ95kMjIGdN3ooO5g/YvZJZTn9KQ/dFmNDPaSyseT9/MCE2Rp0g0\\nT2yCwewxVdfR+D4QcicaLat7CAFXMnoSxV9ifGXYkv6JE33dc95U4BgPYECca2QA\\nNe3ZQHSNxC1rc+uim3cgcn6PP4WKTgTG4u74F9xA8FbumZUIMB7rChsr+E8Z4/Iq\\nSGb7/y0J6Auho7BDJPL7ZFE9peuSA3NudZlpkH+GIWRW7fBY7qu1Koh8kGfZeg4k\\nLIucgT0CpSvrxaDVqRSEIiqy4zoczrLXcVJN9ThtxVPJZCVTW2dir8aCwO9Sk60W\\nQ8DP4wRWhnqa+Irsd/2r5c7QgSSOSo4/EIBU65qP5oA6xUw3F4sE7G+Q9ofijxxJ\\n8iGr2Y7SBk5ztvSYAX/ZNfoUJZQ0cXDCSCKus8a9kQQ9zCNBLco9pIv7XUcJf1bj\\nzfz1o911OZn/mdcgqUq0uRne8x73J/Z4uIsaVubvcSgv0XTSF8qYgpgRd016+nAa\\nFtyVd9Y5xmYgIIHaXzQ3l7MCAwEAAQ==\\n-----END PUBLIC KEY-----\"\n      }\n  ]\n}\n"
  },
  {
    "path": "scripts/setup-cluster.sh",
    "content": "set -eu\n\n# this script must be run from the top-level of the repo\ncd \"$(git rev-parse --show-toplevel)\"\n\n\nDEFAULT_SINGLESTORE_VERSION=\"\"\nVERSION=\"${SINGLESTORE_VERSION:-$DEFAULT_SINGLESTORE_VERSION}\"\nIMAGE_NAME=\"ghcr.io/singlestore-labs/singlestoredb-dev:0.2.51\"\nCONTAINER_NAME=\"singlestore-integration\"\nSSL_DIR=\"${PWD}/scripts/ssl\"\n\nrm -rf \"${SSL_DIR}\"\nmkdir -p \"${SSL_DIR}\"\n\necho \"Create a Certificate Authority (CA)\"\n\nopenssl genpkey -algorithm RSA -out \"${SSL_DIR}\"/ca-key.pem\nopenssl req -new -x509 -key  \"${SSL_DIR}\"/ca-key.pem -out \"${SSL_DIR}\"/ca-cert.pem -days 365 -subj \"/CN=SingleStoreDBCA\"\n\necho \"Generate the Server Certificate\"\n\nopenssl genpkey -algorithm RSA -out \"${SSL_DIR}\"/server-key.pem\nopenssl req -new -key \"${SSL_DIR}\"/server-key.pem -out \"${SSL_DIR}\"/server-req.csr -subj \"/CN=singlestore-server\"\nopenssl x509 -req -in \"${SSL_DIR}\"/server-req.csr -CA \"${SSL_DIR}\"/ca-cert.pem -CAkey \"${SSL_DIR}\"/ca-key.pem -CAcreateserial -out \"${SSL_DIR}\"/server-cert.pem -days 365\n\necho \"Create truststore\"\nkeytool -import -trustcacerts -file \"${SSL_DIR}\"/ca-cert.pem -keystore \"${SSL_DIR}\"/truststore.jks -storepass password -alias singlestore-ca -noprompt\n\necho \"Generate the Client Certificate\"\n\nopenssl genpkey -algorithm RSA -out \"${SSL_DIR}\"/client-key.pem\nopenssl req -new -key \"${SSL_DIR}\"/client-key.pem -out \"${SSL_DIR}\"/client-req.csr -subj \"/CN=singlestore-client\"\nopenssl x509 -req -in \"${SSL_DIR}\"/client-req.csr -CA \"${SSL_DIR}\"/ca-cert.pem -CAkey \"${SSL_DIR}\"/ca-key.pem -CAcreateserial -out \"${SSL_DIR}\"/client-cert.pem -days 365\n\necho \"Create keystore\"\nopenssl pkcs12 -export -inkey \"${SSL_DIR}\"/client-key.pem -in \"${SSL_DIR}\"/client-cert.pem -out \"${SSL_DIR}\"/client-keystore.p12 -name client-cert -CAfile \"${SSL_DIR}\"/ca-cert.pem -caname root -passout pass:password\n\nchmod -R 777 \"${SSL_DIR}\"\n\nEXISTS=$(docker inspect ${CONTAINER_NAME} >/dev/null 2>&1 && echo 1 || echo 0)\n\nif [[ \"${EXISTS}\" -eq 1 ]]; then\n  EXISTING_IMAGE_NAME=$(docker inspect -f '{{.Config.Image}}' ${CONTAINER_NAME})\n  if [[ \"${IMAGE_NAME}\" != \"${EXISTING_IMAGE_NAME}\" ]]; then\n    echo \"Existing container ${CONTAINER_NAME} has image ${EXISTING_IMAGE_NAME} when ${IMAGE_NAME} is expected; recreating container.\"\n    docker rm -f ${CONTAINER_NAME}\n    EXISTS=0\n  fi\nfi\n\nif [[ \"${EXISTS}\" -eq 0 ]]; then\n    docker run -d \\\n        --name ${CONTAINER_NAME} \\\n        -v ${PWD}/scripts/ssl:/test-ssl \\\n        -v ${PWD}/scripts/jwt:/test-jwt \\\n        -e SINGLESTORE_LICENSE=${SINGLESTORE_LICENSE} \\\n        -e ROOT_PASSWORD=${ROOT_PASSWORD} \\\n        -e SINGLESTORE_VERSION=${VERSION} \\\n        -p 5506:3306 -p 5507:3307 -p 5508:3308 \\\n        ${IMAGE_NAME}\nfi\n\n\nsinglestore-wait-start() {\n  echo -n \"Waiting for SingleStore to start...\"\n  while true; do\n      if mysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" -e \"select 1\" >/dev/null 2>/dev/null; then\n          break\n      fi\n      echo -n \".\"\n      sleep 0.2\n  done\n  echo \". Success!\"\n}\n\nsinglestore-wait-start\n\nif [[ \"${EXISTS}\" -eq 0 ]]; then\n    echo\n    echo \"Creating aggregator node\"\n    docker exec ${CONTAINER_NAME} memsqlctl create-node --yes --password ${ROOT_PASSWORD} --port 3308\n    docker exec ${CONTAINER_NAME} memsqlctl update-config --yes --all --key minimum_core_count --value 0\n    docker exec ${CONTAINER_NAME} memsqlctl update-config --yes --all --key minimum_memory_mb --value 0\n    docker exec ${CONTAINER_NAME} memsqlctl start-node --yes --all\n    docker exec ${CONTAINER_NAME} memsqlctl add-aggregator --yes --host 127.0.0.1 --password ${ROOT_PASSWORD} --port 3308\nfi\n\necho\necho \"Setting up SSL\"\ndocker exec ${CONTAINER_NAME} memsqlctl update-config --yes --all --key ssl_ca --value /test-ssl/ca-cert.pem\ndocker exec ${CONTAINER_NAME} memsqlctl update-config --yes --all --key ssl_cert --value /test-ssl/server-cert.pem\ndocker exec ${CONTAINER_NAME} memsqlctl update-config --yes --all --key ssl_key --value /test-ssl/server-key.pem\necho \"Setting up JWT\"\ndocker exec ${CONTAINER_NAME} memsqlctl update-config --yes --all --key jwt_auth_config_file --value /test-jwt/jwt_auth_config.json\necho \"Restarting cluster\"\ndocker restart ${CONTAINER_NAME}\nsinglestore-wait-start\necho \"Setting up root-ssl user\"\nmysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" -e 'create user \"root-ssl\"@\"%\" require ssl'\nmysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" -e 'grant all privileges on *.* to \"root-ssl\"@\"%\" require ssl with grant option'\nmysql -u root -h 127.0.0.1 -P 5507 -p\"${ROOT_PASSWORD}\" -e 'create user \"root-ssl\"@\"%\" require ssl'\nmysql -u root -h 127.0.0.1 -P 5507 -p\"${ROOT_PASSWORD}\" -e 'grant all privileges on *.* to \"root-ssl\"@\"%\" require ssl with grant option'\nmysql -u root -h 127.0.0.1 -P 5508 -p\"${ROOT_PASSWORD}\" -e 'grant all privileges on *.* to \"root-ssl\"@\"%\" require ssl with grant option'\necho \"Done!\"\necho \"Setting up root-jwt user\"\nmysql -h 127.0.0.1 -u root -P 5506 -p\"${ROOT_PASSWORD}\" -e \"CREATE USER 'test_jwt_user' IDENTIFIED WITH authentication_jwt\"\nmysql -h 127.0.0.1 -u root -P 5506 -p\"${ROOT_PASSWORD}\" -e \"GRANT ALL PRIVILEGES ON *.* TO 'test_jwt_user'@'%'\"\necho \"Done!\"\n\nsleep 0.5\necho\necho \"Ensuring child nodes are connected using container IP\"\nCONTAINER_IP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${CONTAINER_NAME})\nCURRENT_LEAF_IP=$(mysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" --batch -N -e 'select host from information_schema.leaves')\nif [[ ${CONTAINER_IP} != \"${CURRENT_LEAF_IP}\" ]]; then\n    # remove leaf with current ip\n    mysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" --batch -N -e \"remove leaf '${CURRENT_LEAF_IP}':3307\"\n    # add leaf with correct ip\n    mysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" --batch -N -e \"add leaf root:'${ROOT_PASSWORD}'@'${CONTAINER_IP}':3307\"\nfi\nCURRENT_AGG_IP=$(mysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" --batch -N -e 'select host from information_schema.aggregators where master_aggregator=0')\nif [[ ${CONTAINER_IP} != \"${CURRENT_AGG_IP}\" ]]; then\n    # remove aggregator with current ip\n    mysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" --batch -N -e \"remove aggregator '${CURRENT_AGG_IP}':3308\"\n    # add aggregator with correct ip\n    mysql -u root -h 127.0.0.1 -P 5506 -p\"${ROOT_PASSWORD}\" --batch -N -e \"add aggregator root:'${ROOT_PASSWORD}'@'${CONTAINER_IP}':3308\"\nfi\n\necho \"Done!\"\n"
  },
  {
    "path": "src/main/resources/META-INF/services/org.apache.spark.sql.sources.DataSourceRegister",
    "content": "com.singlestore.spark.DefaultSource\ncom.memsql.spark.DefaultSource\n"
  },
  {
    "path": "src/main/scala/com/memsql/spark/DefaultSource.scala",
    "content": "package com.memsql.spark\n\nimport com.singlestore.spark\n\nclass DefaultSource extends spark.DefaultSource {\n\n  override def shortName(): String = spark.DefaultSource.MEMSQL_SOURCE_NAME_SHORT\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/AggregatorParallelReadListener.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Connection, SQLException}\nimport java.util.Properties\nimport com.singlestore.spark.JdbcHelpers.getDDLConnProperties\nimport com.singlestore.spark.SQLGen.VariableList\nimport org.apache.spark.SparkContext\nimport org.apache.spark.scheduler.{\n  SparkListener,\n  SparkListenerStageCompleted,\n  SparkListenerStageSubmitted\n}\nimport org.apache.spark.sql.types.StructType\n\nimport scala.collection.mutable\n\nclass AggregatorParallelReadListener(applicationId: String) extends SparkListener with LazyLogging {\n  // connectionsMap is a map from the result table name to the connection with which this table was created\n  private val connectionsMap: mutable.Map[String, Connection] =\n    new mutable.HashMap[String, Connection]()\n\n  // rddInfos is a map from RDD id to the info needed to create result table for this RDD\n  private val rddInfos: mutable.Map[Int, SingleStoreRDDInfo] =\n    new mutable.HashMap[Int, SingleStoreRDDInfo]()\n\n  // SingleStoreRDDInfo is information needed to create a result table\n  private case class SingleStoreRDDInfo(sc: SparkContext,\n                                        query: String,\n                                        variables: VariableList,\n                                        schema: StructType,\n                                        connectionProperties: Properties,\n                                        materialized: Boolean,\n                                        needsRepartition: Boolean,\n                                        repartitionColumns: Seq[String])\n\n  def addRDDInfo(rdd: SinglestoreRDD): Unit = {\n    rddInfos.synchronized({\n      rddInfos += (rdd.id -> SingleStoreRDDInfo(\n        rdd.sparkContext,\n        rdd.query,\n        rdd.variables,\n        rdd.schema,\n        getDDLConnProperties(rdd.options, isOnExecutor = false),\n        rdd.parallelReadType.contains(ReadFromAggregatorsMaterialized),\n        rdd.options.parallelReadRepartition,\n        rdd.parallelReadRepartitionColumns,\n      ))\n    })\n  }\n\n  def deleteRDDInfo(rdd: SinglestoreRDD): Unit = {\n    rddInfos.synchronized({\n      rddInfos -= rdd.id\n    })\n  }\n\n  def isEmpty: Boolean = {\n    rddInfos.synchronized({\n      rddInfos.isEmpty\n    })\n  }\n\n  override def onStageSubmitted(stageSubmitted: SparkListenerStageSubmitted): Unit = {\n    stageSubmitted.stageInfo.rddInfos.foreach(rddInfo => {\n      if (rddInfo.name.startsWith(\"SingleStoreRDD\")) {\n        rddInfos\n          .synchronized(\n            rddInfos.get(rddInfo.id)\n          )\n          .foreach(singleStoreRDDInfo => {\n            val stageId       = stageSubmitted.stageInfo.stageId\n            val attemptNumber = stageSubmitted.stageInfo.attemptNumber()\n            val randHex       = rddInfo.name.substring(\"SingleStoreRDD\".size)\n            val tableName =\n              JdbcHelpers\n                .getResultTableName(applicationId, stageId, rddInfo.id, attemptNumber, randHex)\n\n            // Create connection and save it in the map\n            val conn =\n              SinglestoreConnectionPool.getConnection(singleStoreRDDInfo.connectionProperties)\n            connectionsMap.synchronized(\n              connectionsMap += (tableName -> conn)\n            )\n\n            log.info(s\"Creating result table '$tableName'\")\n            try {\n              // Create result table\n              JdbcHelpers.createResultTable(\n                conn,\n                tableName,\n                singleStoreRDDInfo.query,\n                singleStoreRDDInfo.schema,\n                singleStoreRDDInfo.variables,\n                singleStoreRDDInfo.materialized,\n                singleStoreRDDInfo.needsRepartition,\n                singleStoreRDDInfo.repartitionColumns\n              )\n              log.info(s\"Successfully created result table '$tableName'\")\n            } catch {\n              // Cancel execution if we failed to create a result table\n              case e: SQLException => {\n                singleStoreRDDInfo.sc.cancelStage(stageId)\n                throw e\n              }\n            }\n          })\n      }\n    })\n  }\n\n  override def onStageCompleted(stageCompleted: SparkListenerStageCompleted): Unit = {\n    stageCompleted.stageInfo.rddInfos.foreach(rddInfo => {\n      if (rddInfo.name.startsWith(\"SingleStoreRDD\")) {\n        val stageId       = stageCompleted.stageInfo.stageId\n        val attemptNumber = stageCompleted.stageInfo.attemptNumber()\n        val randHex       = rddInfo.name.substring(\"SingleStoreRDD\".size)\n        val tableName =\n          JdbcHelpers.getResultTableName(applicationId, stageId, rddInfo.id, attemptNumber, randHex)\n\n        connectionsMap.synchronized(\n          connectionsMap\n            .get(tableName)\n            .foreach(conn => {\n              // Drop result table\n              log.info(s\"Dropping result table '$tableName'\")\n              JdbcHelpers.dropResultTable(conn, tableName)\n              log.info(s\"Successfully dropped result table '$tableName'\")\n              // Close connection\n              conn.close()\n              // Delete connection from map\n              connectionsMap -= tableName\n            })\n        )\n      }\n    })\n  }\n}\n\ncase object AggregatorParallelReadListenerAdder {\n  // listeners is a map from SparkContext hash code to the listener associated with this SparkContext\n  private val listeners = new mutable.HashMap[SparkContext, AggregatorParallelReadListener]()\n\n  def addRDD(rdd: SinglestoreRDD): Unit = {\n    this.synchronized({\n      val listener = listeners.getOrElse(\n        rdd.sparkContext, {\n          val newListener = new AggregatorParallelReadListener(rdd.sparkContext.applicationId)\n          rdd.sparkContext.addSparkListener(newListener)\n          listeners += (rdd.sparkContext -> newListener)\n          newListener\n        }\n      )\n      listener.addRDDInfo(rdd)\n    })\n  }\n\n  def deleteRDD(rdd: SinglestoreRDD): Unit = {\n    this.synchronized({\n      listeners\n        .get(rdd.sparkContext)\n        .foreach(listener => {\n          listener.deleteRDDInfo(rdd)\n          if (listener.isEmpty) {\n            listeners -= rdd.sparkContext\n            rdd.sparkContext.removeSparkListener(listener)\n          }\n        })\n    })\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/AvroSchemaHelper.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.avro.Schema\nimport org.apache.avro.Schema.Type\nimport org.apache.avro.Schema.Type._\n\nimport scala.collection.JavaConverters._\n\nobject AvroSchemaHelper {\n\n  def resolveNullableType(avroType: Schema, nullable: Boolean): Schema = {\n    if (nullable && avroType.getType != NULL) {\n      // avro uses union to represent nullable type.\n      val fields = avroType.getTypes.asScala\n      assert(fields.length == 2)\n      val actualType = fields.filter(_.getType != Type.NULL)\n      assert(actualType.length == 1)\n      actualType.head\n    } else {\n      avroType\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/CompletionIterator.scala",
    "content": "package com.singlestore.spark\n\n// Copied from spark's CompletionIterator which is private even though it is generically useful\n\nabstract class CompletionIterator[+A, +I <: Iterator[A]](sub: I) extends Iterator[A] {\n  private[this] var completed = false\n  def next(): A               = sub.next()\n  def hasNext: Boolean = {\n    val r = sub.hasNext\n    if (!r && !completed) {\n      completed = true\n      completion()\n    }\n    r\n  }\n\n  def completion(): Unit\n}\n\nprivate[spark] object CompletionIterator {\n  def apply[A, I <: Iterator[A]](sub: I, completionFunction: => Unit): CompletionIterator[A, I] = {\n    new CompletionIterator[A, I](sub) {\n      def completion(): Unit = completionFunction\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/DefaultSource.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.SQLGenContext\nimport org.apache.spark.TaskContext\nimport org.apache.spark.sql.catalyst.util.CaseInsensitiveMap\nimport org.apache.spark.metrics.source.MetricsHandler\nimport org.apache.spark.sql.sources.{\n  BaseRelation,\n  CreatableRelationProvider,\n  DataSourceRegister,\n  RelationProvider\n}\nimport org.apache.spark.sql.{DataFrame, Row, SQLContext, SaveMode}\n\nobject DefaultSource {\n\n  val SINGLESTORE_SOURCE_NAME          = \"com.singlestore.spark\"\n  val SINGLESTORE_SOURCE_NAME_SHORT    = \"singlestore\"\n  val SINGLESTORE_GLOBAL_OPTION_PREFIX = \"spark.datasource.singlestore.\"\n\n  @Deprecated val MEMSQL_SOURCE_NAME          = \"com.memsql.spark\"\n  @Deprecated val MEMSQL_SOURCE_NAME_SHORT    = \"memsql\"\n  @Deprecated val MEMSQL_GLOBAL_OPTION_PREFIX = \"spark.datasource.memsql.\"\n}\n\nclass DefaultSource\n    extends RelationProvider\n    with DataSourceRegister\n    with CreatableRelationProvider\n    with LazyLogging {\n\n  override def shortName(): String = DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT\n\n  private def includeGlobalParams(sqlContext: SQLContext,\n                                  params: Map[String, String]): Map[String, String] =\n    sqlContext.getAllConfs.foldLeft(params)({\n      case (params, (k, v)) if k.startsWith(DefaultSource.SINGLESTORE_GLOBAL_OPTION_PREFIX) =>\n        params + (k.stripPrefix(DefaultSource.SINGLESTORE_GLOBAL_OPTION_PREFIX) -> v)\n      case (params, (k, v)) if k.startsWith(DefaultSource.MEMSQL_GLOBAL_OPTION_PREFIX) =>\n        params + (k.stripPrefix(DefaultSource.MEMSQL_GLOBAL_OPTION_PREFIX) -> v)\n      case (params, _) => params\n    })\n\n  override def createRelation(sqlContext: SQLContext,\n                              parameters: Map[String, String]): BaseRelation = {\n    val params  = CaseInsensitiveMap(includeGlobalParams(sqlContext, parameters))\n    val options = SinglestoreOptions(params, sqlContext.sparkSession.sparkContext)\n    if (options.disablePushdown) {\n      SQLPushdownRule.ensureRemoved(sqlContext.sparkSession)\n      SinglestoreReaderNoPushdown(SinglestoreOptions.getQuery(params), options, sqlContext)\n    } else {\n      SQLPushdownRule.ensureInjected(sqlContext.sparkSession)\n      SinglestoreReader(SinglestoreOptions.getQuery(params),\n                        Nil,\n                        options,\n                        sqlContext,\n                        context = SQLGenContext(options))\n    }\n  }\n\n  override def createRelation(sqlContext: SQLContext,\n                              mode: SaveMode,\n                              parameters: Map[String, String],\n                              data: DataFrame): BaseRelation = {\n    val opts = CaseInsensitiveMap(includeGlobalParams(sqlContext, parameters))\n    val conf = SinglestoreOptions(opts, sqlContext.sparkSession.sparkContext)\n\n    val table = SinglestoreOptions\n      .getTable(opts)\n      .getOrElse(\n        throw new IllegalArgumentException(\n          s\"To write a dataframe to SingleStore you must specify a table name via the '${SinglestoreOptions.TABLE_NAME}' parameter\"\n        )\n      )\n    JdbcHelpers.prepareTableForWrite(conf, table, mode, data.schema)\n    val isReferenceTable = JdbcHelpers.isReferenceTable(conf, table)\n    val partitionWriterFactory =\n      if (conf.onDuplicateKeySQL.isEmpty) {\n        new LoadDataWriterFactory(table, conf)\n      } else {\n        new BatchInsertWriterFactory(table, conf)\n      }\n\n    val schema        = data.schema\n    var totalRowCount = 0L\n    data.foreachPartition((partition: Iterator[Row]) => {\n      val writer = partitionWriterFactory.createDataWriter(schema,\n                                                           TaskContext.getPartitionId(),\n                                                           0,\n                                                           isReferenceTable,\n                                                           mode)\n      try {\n        partition.foreach(record => {\n          writer.write(record)\n          totalRowCount += 1\n        })\n        writer.commit()\n        MetricsHandler.setRecordsWritten(totalRowCount)\n      } catch {\n        case e: Exception =>\n          writer.abort(e)\n          throw e\n      }\n    })\n\n    createRelation(sqlContext, parameters)\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/ExpressionGen.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.aggregate._\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.catalyst.util.DateTimeUtils\nimport org.apache.spark.sql.types._\nimport org.apache.spark.unsafe.types.{CalendarInterval, UTF8String}\n\nimport scala.reflect.ClassTag\n\nobject ExpressionGen extends LazyLogging {\n  import SQLGen._\n\n  final val SINGLESTORE_DECIMAL_MAX_PRECISION = 65\n  final val SINGLESTORE_DECIMAL_MAX_SCALE     = 30\n  final val SINGLESTORE_DEFAULT_TIME_FORMAT   = UTF8String.fromString(\"yyyy-MM-dd HH:mm:ss\")\n\n  // DAYS_OF_WEEK_OFFSET_MAP is a map from week day prefix to it's offset (sunday -> 1, saturday -> 7)\n  final val DAYS_OF_WEEK_OFFSET_MAP: Map[String, String] = {\n    val daysOfWeek =\n      List(\"sunday\", \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\")\n    val prefix2    = daysOfWeek.map(day => day.slice(0, 2))\n    val prefix3    = daysOfWeek.map(day => day.slice(0, 3))\n    val dayFormats = daysOfWeek ::: prefix3 ::: prefix2\n\n    dayFormats.zipWithIndex.map { case (day, ind) => (day, (ind % 7 + 1).toString) }.toMap\n  }\n\n  // helpers to keep this code sane\n  def f(n: String, c: Joinable*): Statement              = func(n, c: _*)\n  def op(o: String, l: Joinable, r: Joinable): Statement = block(l + o + r)\n  def ifNeg(value: Joinable, valTrue: Joinable, valFalse: Joinable): Statement =\n    f(\"IF\", op(\"<\", value, IntVar(0)), valTrue, valFalse)\n\n  def makeDecimal(child: Joinable, precision: Int, scale: Int): Joinable = {\n    val p = Math.min(SINGLESTORE_DECIMAL_MAX_PRECISION, precision)\n    val s = Math.min(SINGLESTORE_DECIMAL_MAX_SCALE, scale)\n    cast(child, s\"DECIMAL($p, $s)\")\n  }\n\n  def addMicroseconds(start: Joinable, v: CalendarInterval): Joinable =\n    addMicroseconds(start, v.microseconds)\n\n  def addMicroseconds(start: Joinable, v: Long): Joinable =\n    if (v == 0) {\n      start\n    } else {\n      f(\"DATE_ADD\", start, Raw(\"INTERVAL\") + v.toString + \"MICROSECOND\")\n    }\n\n  def addMonths(start: Joinable, v: CalendarInterval): Joinable =\n    addMonths(start, v.months)\n\n  def addMonths(start: Joinable, v: Int): Joinable =\n    if (v == 0) {\n      start\n    } else {\n      f(\"DATE_ADD\", start, Raw(\"INTERVAL\") + v.toString + \"MONTH\")\n    }\n\n  def subMicroseconds(start: Joinable, v: CalendarInterval): Joinable =\n    if (v.microseconds == 0) {\n      start\n    } else {\n      f(\"DATE_SUB\", start, Raw(\"INTERVAL\") + v.microseconds.toString + \"MICROSECOND\")\n    }\n\n  def subMonths(start: Joinable, v: CalendarInterval): Joinable =\n    if (v.months == 0) {\n      start\n    } else {\n      f(\"DATE_SUB\", start, Raw(\"INTERVAL\") + v.months.toString + \"MONTH\")\n    }\n\n  def longToDecimal(child: Joinable, p: Int, s: Int): Joinable =\n    makeDecimal(op(\"/\", child, math.pow(10.0, s).toString), p, s)\n\n  def like(left: Joinable, right: Joinable): Joinable =\n    op(\"LIKE\", left, right)\n\n  def likePatterns(child: Joinable, patterns: Seq[UTF8String], operation: String): Joinable = {\n    patterns.foldLeft(op(\"LIKE\", child, s\"'${patterns.head.toString}'\"))((patternA, patternB) => {\n      op(operation, patternA, op(\"LIKE\", child, s\"'${patternB.toString}'\"))\n    })\n  }\n\n  // regexpFromStart adds a ^ prefix for singlestore regexp to match the beginning of the string (as Java does)\n  def regexpFromStart(r: Joinable): Joinable = func(\"CONCAT\", StringVar(\"^\"), r)\n\n  // computeNextDay returns a statement for computing the next date after startDate with specified offset (sunday -> 1, saturday -> 7)\n  // ADDDATE(startDate,(dayOfWeekOffset - DAYOFWEEK(startDate) + 6)%7 +1)\n  def computeNextDay(startDate: Joinable, offset: Joinable): Statement = f(\n    \"ADDDATE\",\n    startDate,\n    op(\n      \"+\",\n      op(\n        \"%\",\n        op(\n          \"+\",\n          op(\n            \"-\",\n            offset,\n            f(\"DAYOFWEEK\", startDate)\n          ),\n          \"6\"\n        ),\n        \"7\"\n      ),\n      \"1\"\n    )\n  )\n\n  object GenLiteral {\n    def unapply(arg: Expression): Option[Joinable] = arg match {\n      case Literal(v, _) if v == null      => Some(StringVar(null))\n      case Literal(v: Int, DateType)       => Some(DateVar(DateTimeUtils.toJavaDate(v)))\n      case Literal(v: Long, TimestampType) => Some(TimestampVar(DateTimeUtils.toJavaTimestamp(v)))\n\n      case Literal(v, t) if !VersionSpecificUtil.isIntervalType(t) => {\n        convertLiteralValue.lift(v)\n      }\n\n      case _ => None\n    }\n\n    def unapply(vals: Iterable[Any]): Option[Joinable] =\n      vals\n        .map(convertLiteralValue.lift)\n        .reduce[Option[Joinable]] {\n          case (Some(left), Some(right)) => Some(left + \",\" + right)\n          case _                         => None\n        }\n\n    def convertLiteralValue: PartialFunction[Any, Joinable] = {\n      case v if v == null => StringVar(null)\n\n      case v: String     => StringVar(v)\n      case v: UTF8String => StringVar(v.toString)\n      case v: Byte       => ByteVar(v)\n\n      case v: Boolean => Raw(if (v) \"TRUE\" else \"FALSE\")\n\n      case v: Short                                  => Raw(v.toString)\n      case v: Int                                    => Raw(v.toString)\n      case v: Integer                                => Raw(v.toString)\n      case v: Long                                   => Raw(v.toString)\n      case v: Decimal                                => makeDecimal(Raw(v.toString), v.precision, v.scale)\n      case v: BigDecimal                             => makeDecimal(Raw(v.toString), v.precision, v.scale)\n      case v: Float if java.lang.Float.isFinite(v)   => Raw(v.toString)\n      case v: Double if java.lang.Double.isFinite(v) => Raw(v.toString)\n    }\n  }\n\n  case class FoldableExtractor[T]() {\n    def unapply(e: Expression)(implicit tag: ClassTag[T]): Option[T] =\n      if (e.foldable) {\n        e.eval() match {\n          case expr: T =>\n            tag.unapply(expr)\n          case _ => None\n        }\n      } else None\n  }\n\n  case class DecimalExpressionExtractor(expressionExtractor: ExpressionExtractor) {\n    def unapply(e: Expression): Option[(Joinable, Int, Int)] = (e, e.dataType) match {\n      case (expressionExtractor(child), t: DecimalType) => Some((child, t.precision, t.scale))\n      case _                                            => None\n    }\n  }\n\n  case class WindowBoundaryExpressionExtractor(expressionExtractor: ExpressionExtractor) {\n    val versionSpecificWindowBoundaryExpressionExtractor\n      : VersionSpecificWindowBoundaryExpressionExtractor =\n      VersionSpecificWindowBoundaryExpressionExtractor(expressionExtractor)\n    def unapply(arg: Expression): Option[Joinable] = arg match {\n      case versionSpecificWindowBoundaryExpressionExtractor(statement) => Some(statement)\n      case e: SpecialFrameBoundary                                     => Some(e.sql)\n      case Literal(n: Integer, IntegerType) =>\n        Some(Raw(Math.abs(n).toString) + (if (n < 0) \"PRECEDING\" else \"FOLLOWING\"))\n      case expressionExtractor(child) => Some(child + \"FOLLOWING\")\n      case _                          => None\n    }\n  }\n\n  case class AggregateExpressionExtractor(expressionExtractor: ExpressionExtractor,\n                                          context: SQLGenContext) {\n    def unapply(arg: AggregateExpression): Option[Joinable] = {\n      val filterOption = arg.filter match {\n        case None => Some(None)\n        case Some(filter) =>\n          expressionExtractor\n            .unapply(filter)\n            .map(f => Some(f))\n      }\n      filterOption.flatMap(filter => {\n        val versionSpecificAggregateExpressionExtractor =\n          VersionSpecificAggregateExpressionExtractor(expressionExtractor, context, filter)\n\n        arg.aggregateFunction match {\n          case Count(expression) =>\n            (expression, arg.isDistinct, filter) match {\n              case (expressionExtractor(None), false, filter) =>\n                Some(aggregateWithFilter(\"COUNT\", \"1\", filter))\n              case (expressionExtractor(Some(children)), false, filter) =>\n                Some(aggregateWithFilter(\"COUNT\", children, filter))\n              // DISTINCT and FILTER can't be used together\n              case (expressionExtractor(Some(children)), true, None) =>\n                Some(Raw(\"COUNT\") + block(Raw(\"DISTINCT\") + children))\n              case _ => None\n            }\n\n          // Covariance.scala\n          // TODO: case CovPopulation(expressionExtractor(left), expressionExtractor(right)) => ???\n          // TODO: case CovSample(expressionExtractor(left), expressionExtractor(right))     => ???\n\n          // Max.scala\n          case Max(expressionExtractor(child)) =>\n            Some(aggregateWithFilter(\"MAX\", child, filter))\n\n          // Min.scala\n          case Min(expressionExtractor(child)) =>\n            Some(aggregateWithFilter(\"MIN\", child, filter))\n\n          // BitAnd.scala\n          case BitAndAgg(expressionExtractor(child))\n              if context.singlestoreVersionAtLeast(\"7.0.1\") =>\n            Some(ExpressionGen.aggregateWithFilter(\"BIT_AND\", child, filter))\n          // BitOr.scala\n          case BitOrAgg(expressionExtractor(child)) if context.singlestoreVersionAtLeast(\"7.0.1\") =>\n            Some(ExpressionGen.aggregateWithFilter(\"BIT_OR\", child, filter))\n          // BitXor.scala\n          case BitXorAgg(expressionExtractor(child))\n              if context.singlestoreVersionAtLeast(\"7.0.1\") =>\n            Some(ExpressionGen.aggregateWithFilter(\"BIT_XOR\", child, filter))\n\n          case versionSpecificAggregateExpressionExtractor(statement) => Some(statement)\n\n          //    case AggregateExpression(MaxBy(expressionExtractor(valueExpr), expressionExtractor(orderingExpr)), _, _, None, _) =>\n          //    case AggregateExpression(MinBy(expressionExtractor(valueExpr), expressionExtractor(orderingExpr)), _, _, None, _) =>\n          case _ => None\n        }\n      })\n    }\n  }\n\n  // we need to manually unwrap MonthsBetween since the roundOff argument\n  // does not exist in Spark 2.3\n  // The roundOff argument truncates the result to 8 digits of precision\n  // which we can safely ignore, the user can apply an explicit round if needed\n  case class MonthsBetweenExpressionExtractor(expressionExtractor: ExpressionExtractor) {\n    def unapply(arg: MonthsBetween): Option[(Joinable, Joinable)] =\n      for {\n        date1 <- expressionExtractor.unapply(arg.date1)\n        date2 <- expressionExtractor.unapply(arg.date2)\n      } yield (date1, date2)\n  }\n\n  case class CaseWhenExpressionExtractor(expressionExtractor: ExpressionExtractor) {\n    def unapply(arg: CaseWhen): Option[Joinable] = {\n      val condition =\n        arg.branches.foldLeft(Option(stringToJoinable(\"\")))((prefix: Option[Joinable], branch) => {\n          prefix match {\n            case Some(actualPrefix) =>\n              branch match {\n                case (expressionExtractor(whenCondition), expressionExtractor(thenCondition)) =>\n                  Some(actualPrefix + Raw(\"WHEN\") + whenCondition + Raw(\"THEN\") + thenCondition)\n                case _ => None\n              }\n            case None => None\n          }\n        })\n\n      val elseCondition = arg.elseValue match {\n        case Some(expressionExtractor(e)) => Some(Raw(\"ELSE\") + e)\n        case None                         => Some(Raw(\"\"))\n        case _                            => None\n      }\n\n      (condition, elseCondition) match {\n        case (Some(c), Some(e)) => Some(block(Raw(\"CASE\") + c + e + \"END\"))\n        case _                  => None\n      }\n    }\n  }\n\n  def aggregateWithFilter(funcName: String, child: Joinable, filter: Option[Joinable]) = {\n    filter match {\n      case Some(filterExpression) =>\n        f(funcName, f(\"IF\", filterExpression, child, StringVar(null)))\n      case None => f(funcName, child)\n    }\n  }\n\n  val intFoldableExtractor: FoldableExtractor[Int]               = FoldableExtractor[Int]()\n  val utf8StringFoldableExtractor: FoldableExtractor[UTF8String] = FoldableExtractor[UTF8String]()\n\n  def apply(expressionExtractor: ExpressionExtractor): PartialFunction[Expression, Joinable] = {\n    val caseWhenExpressionExtractor       = CaseWhenExpressionExtractor(expressionExtractor)\n    val windowBoundaryExpressionExtractor = WindowBoundaryExpressionExtractor(expressionExtractor)\n    val monthsBetweenExpressionExtractor  = MonthsBetweenExpressionExtractor(expressionExtractor)\n    val context                           = expressionExtractor.context\n    val aggregateExpressionExtractor      = AggregateExpressionExtractor(expressionExtractor, context)\n    val decimalExpressionExtractor        = DecimalExpressionExtractor(expressionExtractor)\n    val versionSpecificExpressionGen      = VersionSpecificExpressionGen(expressionExtractor)\n\n    return {\n      // ----------------------------------\n      // Attributes\n      // ----------------------------------\n      case a: Attribute => Attr(a, context)\n      case a @ Alias(expressionExtractor(child), name) =>\n        alias(child, name, a.exprId, context)\n\n      // ----------------------------------\n      // Literals\n      // ----------------------------------\n      case GenLiteral(v) => v\n\n      // ----------------------------------\n      // Variable Expressions\n      // ----------------------------------\n\n      case Coalesce(expressionExtractor(Some(child))) => f(\"COALESCE\", child)\n      case Least(expressionExtractor(Some(child)))    => f(\"LEAST\", child)\n      case Greatest(expressionExtractor(Some(child))) => f(\"GREATEST\", child)\n      case Concat(expressionExtractor(Some(child)))   => f(\"CONCAT\", child)\n\n      // ----------------------------------\n      // Aggregate Expressions\n      // ----------------------------------\n      case aggregateExpressionExtractor(expression) => expression\n\n      // windowExpressions.scala\n      case WindowExpression(expressionExtractor(child),\n                            WindowSpecDefinition(expressionExtractor(partitionSpec),\n                                                 expressionExtractor(orderSpec),\n                                                 expressionExtractor(frameSpec))) =>\n        child + \"OVER\" + block(\n          partitionSpec.map(Raw(\"PARTITION BY\") + _).getOrElse(empty) +\n            orderSpec.map(Raw(\"ORDER BY\") + _).getOrElse(empty) +\n            frameSpec\n        )\n\n      case UnspecifiedFrame => \"\"\n\n      case SpecifiedWindowFrame(frameType,\n                                windowBoundaryExpressionExtractor(lower),\n                                windowBoundaryExpressionExtractor(upper)) =>\n        Raw(frameType.sql) + \"BETWEEN\" + lower + \"AND\" + upper\n\n      case RowNumber()                       => \"ROW_NUMBER()\"\n      case NTile(expressionExtractor(child)) => f(\"NTILE\", child)\n      case Rank(_)                           => \"RANK()\"\n      case DenseRank(_)                      => \"DENSE_RANK()\"\n      case PercentRank(_)                    => \"PERCENT_RANK()\"\n\n      // TODO: case CumeDist()               => ???\n\n      // ----------------------------------\n      // Binary Expressions\n      // ----------------------------------\n\n      // bitwiseExpressions.scala\n      case BitwiseAnd(expressionExtractor(left), expressionExtractor(right)) => op(\"&\", left, right)\n      case BitwiseOr(expressionExtractor(left), expressionExtractor(right))  => op(\"|\", left, right)\n      case BitwiseXor(expressionExtractor(left), expressionExtractor(right)) => op(\"^\", left, right)\n\n      // datetimeExpressions.scala\n\n      // NOTE: we explicitly ignore the timeZoneId field in all of the following expressions\n      // The user is required to setup Spark and/or SingleStore with the timezone they want or they\n      // will get inconsistent results with/without pushdown.\n\n      case DateAdd(expressionExtractor(startDate), expressionExtractor(days)) =>\n        f(\"ADDDATE\", startDate, days)\n      case DateSub(expressionExtractor(startDate), expressionExtractor(days)) =>\n        f(\"SUBDATE\", startDate, days)\n\n      case TimeAdd(expressionExtractor(start),\n                   Literal(v: CalendarInterval, CalendarIntervalType),\n                   timeZoneId) => {\n        def addDays(start: Joinable) =\n          if (v.days == 0) {\n            start\n          } else {\n            f(\"DATE_ADD\", start, Raw(\"INTERVAL\") + v.days.toString + \"DAY\")\n          }\n\n        addMicroseconds(addDays(addMonths(start, v)), v)\n      }\n\n      case FromUTCTimestamp(expressionExtractor(timestamp), expressionExtractor(timezone)) =>\n        f(\"CONVERT_TZ\", timestamp, StringVar(\"UTC\"), timezone)\n\n      case ToUTCTimestamp(expressionExtractor(timestamp), expressionExtractor(timezone)) =>\n        f(\"CONVERT_TZ\", timestamp, timezone, StringVar(\"UTC\"))\n\n      case TruncTimestamp(expressionExtractor(format),\n                          expressionExtractor(timestamp),\n                          timeZoneId) => {\n        f(\n          \"DATE_TRUNC\",\n          sqlMapValueCaseInsensitive(\n            format,\n            Map(\n              // SingleStore doesn't support formats (\"yyyy\", \"yy\", \"mon\", \"mm\", \"dd\") so we map them here\n              \"yyyy\" -> \"year\",\n              \"yy\"   -> \"year\",\n              \"mon\"  -> \"month\",\n              \"mm\"   -> \"month\",\n              \"dd\"   -> \"day\"\n            ),\n            format\n          ),\n          timestamp\n        )\n      }\n\n      case TruncDate(expressionExtractor(date), expressionExtractor(format)) => {\n        f(\n          \"DATE_TRUNC\",\n          sqlMapValueCaseInsensitive(\n            format,\n            Map(\n              // SingleStore doesn't support formats (\"yyyy\", \"yy\", \"mon\", \"mm\") so we map them here\n              \"yyyy\" -> \"year\",\n              \"yy\"   -> \"year\",\n              \"mon\"  -> \"month\",\n              \"mm\"   -> \"month\"\n            ),\n            format\n          ),\n          date\n        )\n      }\n\n      case monthsBetweenExpressionExtractor((date1, date2)) =>\n        f(\"MONTHS_BETWEEN\", date1, date2)\n\n      case caseWhenExpressionExtractor(caseWhenStatement) => caseWhenStatement\n\n      case AddMonths(expressionExtractor(startDate), expressionExtractor(numMonths)) =>\n        f(\"DATE_ADD\", startDate, Raw(\"INTERVAL\") + numMonths + \"MONTH\")\n\n      // SingleStore and spark support other date formats\n      // UnixTime doesn't use format if time is already a dataType or TimestampType\n\n      case FromUnixTime(expressionExtractor(sec), utf8StringFoldableExtractor(format), timeZoneId)\n          if format == SINGLESTORE_DEFAULT_TIME_FORMAT =>\n        f(\"FROM_UNIXTIME\", sec)\n\n      case DateDiff(expressionExtractor(endDate), expressionExtractor(startDate)) =>\n        f(\"DATEDIFF\", endDate, startDate)\n\n      // mathExpressions.scala\n      case Atan2(expressionExtractor(left), expressionExtractor(right))     => f(\"ATAN2\", left, right)\n      case Pow(expressionExtractor(left), expressionExtractor(right))       => f(\"POWER\", left, right)\n      case ShiftLeft(expressionExtractor(left), expressionExtractor(right)) => op(\"<<\", left, right)\n      case ShiftRight(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\">>\", left, right)\n      case ShiftRightUnsigned(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\">>\", left, right)\n      case Logarithm(expressionExtractor(left), expressionExtractor(right)) => f(\"LOG\", left, right)\n      case Hypot(expressionExtractor(left), expressionExtractor(right)) =>\n        f(\"SQRT\", op(\"+\", f(\"POW\", left, \"2\"), f(\"POW\", right, \"2\")))\n\n      // TODO: case _: BRound => None\n\n      // predicates.scala\n      case And(expressionExtractor(left), expressionExtractor(right)) => op(\"AND\", left, right)\n      case Or(expressionExtractor(left), expressionExtractor(right))  => op(\"OR\", left, right)\n\n      case EqualTo(expressionExtractor(left), expressionExtractor(right)) => op(\"=\", left, right)\n      case EqualNullSafe(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\"<=>\", left, right)\n      case LessThan(expressionExtractor(left), expressionExtractor(right)) => op(\"<\", left, right)\n      case LessThanOrEqual(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\"<=\", left, right)\n      case GreaterThan(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\">\", left, right)\n      case GreaterThanOrEqual(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\">=\", left, right)\n\n      case If(expressionExtractor(predicate),\n              expressionExtractor(trueValue),\n              expressionExtractor(falseValue)) =>\n        f(\"IF\", predicate, trueValue, falseValue)\n\n      case In(expressionExtractor(child), expressionExtractor(Some(elements))) =>\n        op(\"IN\", child, block(elements))\n\n      case InSet(expressionExtractor(child), GenLiteral(elements)) =>\n        op(\n          \"IN\",\n          child,\n          block(elements)\n        )\n\n      // regexpExpressions.scala\n      case Like(expressionExtractor(left), expressionExtractor(right), escapeChar: Char) =>\n        if (escapeChar == '\\\\') {\n          op(\"LIKE\", left, right)\n        } else {\n          op(\"LIKE\", left, f(\"REPLACE\", right, \"'\" + escapeChar.toString + \"'\", \"'\\\\\\\\'\"))\n        }\n      case RLike(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\"RLIKE\", left, regexpFromStart(right))\n\n      // stringExpressions.scala\n      case Contains(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\">\", f(\"INSTR\", left, right), \"0\")\n      case StartsWith(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\"LIKE\", left, f(\"CONCAT\", right, StringVar(\"%\")))\n      case EndsWith(expressionExtractor(left), expressionExtractor(right)) =>\n        op(\"LIKE\", left, f(\"CONCAT\", StringVar(\"%\"), right))\n      case StringInstr(expressionExtractor(str), expressionExtractor(substr)) =>\n        f(\"INSTR\", str, substr)\n      case FormatNumber(expressionExtractor(x), e @ expressionExtractor(d))\n          if e.dataType == IntegerType =>\n        ifNeg(d, StringVar(null), f(\"FORMAT\", x, d))\n      case StringRepeat(expressionExtractor(child), expressionExtractor(times)) =>\n        f(\"LPAD\",\n          StringVar(\"\"),\n          ifNeg(times, IntVar(0), times) + \"*\" + f(\"CHAR_LENGTH\", child),\n          child)\n\n      case StringTrim(expressionExtractor(srcStr), None) =>\n        f(\"TRIM\", Raw(\"BOTH\") + \"FROM\" + srcStr)\n      case StringTrim(expressionExtractor(srcStr), Some(utf8StringFoldableExtractor(trimStr)))\n          if trimStr == UTF8String.fromString(\" \") =>\n        f(\"TRIM\", Raw(\"BOTH\") + \"FROM\" + srcStr)\n\n      case StringTrimLeft(expressionExtractor(srcStr), None) =>\n        f(\"LTRIM\", srcStr)\n      case StringTrimLeft(expressionExtractor(srcStr), Some(utf8StringFoldableExtractor(trimStr)))\n          if trimStr == UTF8String.fromString(\" \") =>\n        f(\"LTRIM\", srcStr)\n\n      case StringTrimRight(expressionExtractor(srcStr), None) =>\n        f(\"RTRIM\", srcStr)\n      case StringTrimRight(expressionExtractor(srcStr), Some(utf8StringFoldableExtractor(trimStr)))\n          if trimStr == UTF8String.fromString(\" \") =>\n        f(\"RTRIM\", srcStr)\n\n      case FindInSet(expressionExtractor(left), utf8StringFoldableExtractor(right)) => {\n        val str_array    = right.toString.split(',')\n        var caseBranches = stringToJoinable(\"\")\n        for (i <- 1 to str_array.length) {\n          caseBranches += Raw(s\"WHEN '${str_array(i - 1)}'\")\n          caseBranches += Raw(s\"THEN '${i.toString}'\")\n        }\n        block(Raw(\"CASE\") + left + caseBranches + Raw(\"ELSE 0 END\"))\n      }\n\n      // TODO: case _: Levenshtein => None\n\n      // ----------------------------------\n      // Leaf Expressions\n      // ----------------------------------\n\n      // datetimeExpressions.scala\n      case CurrentDate(_)     => \"CURRENT_DATE()\"\n      case CurrentTimestamp() => \"NOW(6)\"\n\n      // mathExpressions.scala\n      case EulerNumber() => math.E.toString\n      case Pi()          => \"PI()\"\n\n      // TODO: case RegExpExtract(expressionExtractor(subject), expressionExtractor(regexp), expressionExtractor(idx)) => ???\n\n      // stringExpressions.scala\n      case StringReplace(expressionExtractor(srcExpr),\n                         expressionExtractor(searchExpr),\n                         expressionExtractor(replaceExpr)) =>\n        f(\"REPLACE\", srcExpr, searchExpr, replaceExpr)\n      case SubstringIndex(expressionExtractor(strExpr),\n                          expressionExtractor(delimExpr),\n                          expressionExtractor(countExpr)) =>\n        f(\"SUBSTRING_INDEX\", strExpr, delimExpr, countExpr)\n      case StringLocate(expressionExtractor(substr),\n                        expressionExtractor(str),\n                        expressionExtractor(start)) =>\n        f(\"LOCATE\", substr, str, start)\n      case StringLPad(expressionExtractor(str),\n                      expressionExtractor(len),\n                      expressionExtractor(pad)) =>\n        f(\"LPAD\", str, ifNeg(len, IntVar(0), len), pad)\n      case StringRPad(expressionExtractor(str),\n                      expressionExtractor(len),\n                      expressionExtractor(pad)) =>\n        f(\"RPAD\", str, ifNeg(len, IntVar(0), len), pad)\n      case Substring(expressionExtractor(str),\n                     expressionExtractor(pos),\n                     expressionExtractor(len)) =>\n        f(\"SUBSTR\", str, pos, len)\n\n      case Overlay(expressionExtractor(input),\n                   expressionExtractor(replace),\n                   expressionExtractor(pos),\n                   expressionExtractor(len)) =>\n        f(\n          \"IF\",\n          op(\"<\", len, IntVar(0)),\n          f(\"CONCAT\",\n            f(\"LEFT\", input, op(\"-\", pos, \"1\")),\n            replace,\n            f(\"SUBSTR\", input, op(\"+\", f(\"LENGTH\", replace), pos))),\n          f(\"CONCAT\",\n            f(\"LEFT\", input, op(\"-\", pos, \"1\")),\n            replace,\n            f(\"SUBSTR\", input, op(\"+\", pos, len)))\n        )\n\n      case StringTranslate(expressionExtractor(srcExpr),\n                           utf8StringFoldableExtractor(matchingExpr),\n                           utf8StringFoldableExtractor(replaceExpr)) => {\n        var replaceContent  = srcExpr\n        val replaceExprLen  = replaceExpr.toString.length\n        val matchingExprLen = matchingExpr.toString.length\n        for (i <- 0 to Math.max(replaceExprLen, matchingExprLen) - 1) {\n          val matchingCurrCharacter = if (i < matchingExprLen) {\n            s\"'${matchingExpr.toString.charAt(i)}'\"\n          } else {\n            \"''\"\n          }\n          val replaceCurrCharacter = if (i < replaceExprLen) {\n            s\"'${replaceExpr.toString.charAt(i)}'\"\n          } else {\n            \"''\"\n          }\n          replaceContent = f(\"REPLACE\", replaceContent, matchingCurrCharacter, replaceCurrCharacter)\n        }\n        replaceContent\n      }\n\n      // ----------------------------------\n      // Unary Expressions\n      // ----------------------------------\n\n      // arithmetic.scala\n      case UnaryPositive(expressionExtractor(child)) => f(\"+\", child)\n\n      // bitwiseExpression.scala\n      case BitwiseNot(expressionExtractor(expr)) => f(\"~\", expr)\n\n      case BitwiseCount(expressionExtractor(child)) =>\n        f(\"BIT_COUNT\", child)\n\n      // TODO: case UpCast(expressionExtractor(child), dataType, walkedTypePath) => ???\n\n      // datetimeExpressions.scala\n      case Hour(expressionExtractor(child), _)     => f(\"HOUR\", child)\n      case Minute(expressionExtractor(child), _)   => f(\"MINUTE\", child)\n      case Second(expressionExtractor(child), _)   => f(\"SECOND\", child)\n      case DayOfYear(expressionExtractor(child))   => f(\"DAYOFYEAR\", child)\n      case Year(expressionExtractor(child))        => f(\"YEAR\", child)\n      case Quarter(expressionExtractor(child))     => f(\"QUARTER\", child)\n      case Month(expressionExtractor(child))       => f(\"MONTH\", child)\n      case DayOfMonth(expressionExtractor(child))  => f(\"DAY\", child)\n      case DayOfWeek(expressionExtractor(child))   => f(\"DAYOFWEEK\", child)\n      case WeekDay(expressionExtractor(child))     => f(\"WEEKDAY\", child)\n      case WeekOfYear(expressionExtractor(child))  => f(\"WEEK\", child, \"3\")\n      case LastDay(expressionExtractor(startDate)) => f(\"LAST_DAY\", startDate)\n      case Now()                                   => f(\"NOW\")\n\n      //    case DatePart(expressionExtractor(field), expressionExtractor(source), expressionExtractor(child)) => // Converts to CAST(field)\n      //    case Extract(expressionExtractor(field), expressionExtractor(source), expressionExtractor(child))  => // Converts to CAST(field)\n      //    case MakeInterval(_, _, _, _, _, _, _) => ???\n\n      // MakeDecimal and UnscaledValue value are used in DecimalAggregates optimizer\n      // This optimizer replace Decimals inside of the sum and aggregate expressions to the Longs using UnscaledValue\n      // and then casts the result back to Decimal using MakeDecimal\n      case MakeDecimal(expressionExtractor(child), p, s, _) =>\n        longToDecimal(child, p, s)\n\n      case UnscaledValue(decimalExpressionExtractor(child, precision, scale)) =>\n        op(\"!:>\", op(\"*\", child, math.pow(10.0, scale).toString), \"BIGINT\")\n\n      // hash.scala\n      case Md5(expressionExtractor(child))  => f(\"MD5\", child)\n      case Sha1(expressionExtractor(child)) => f(\"SHA1\", child)\n      case Sha2(expressionExtractor(left), right)\n          if right.foldable &&\n            right.eval().isInstanceOf[Int] &&\n            right.eval().asInstanceOf[Int] != 224 =>\n        f(\"SHA2\", left, right.toString)\n      case Crc32(expressionExtractor(child)) => f(\"CRC32\", child)\n\n      //jsonExpressions.scala\n      case GetJsonObject(expressionExtractor(json), utf8StringFoldableExtractor(path))\n          if path.toString.length >= 2 & path.toString.startsWith(\"$.\") => {\n        val pathParts = path.toString.substring(2).split(\"\\\\.\")\n        val goalPath  = pathParts.last\n        var jsonQuery = json\n        for (i <- 0 to (pathParts.length - 2)) {\n          jsonQuery = f(\"JSON_EXTRACT_JSON\", jsonQuery, StringVar(pathParts(i)))\n        }\n        f(\n          \"IF\",\n          op(\"=\",\n             f(\"JSON_GET_TYPE\", f(\"JSON_EXTRACT_JSON\", jsonQuery, StringVar(goalPath))),\n             StringVar(\"string\")),\n          f(\"JSON_EXTRACT_STRING\", jsonQuery, StringVar(goalPath)),\n          f(\"JSON_EXTRACT_JSON\", jsonQuery, StringVar(goalPath))\n        )\n\n      }\n\n      // mathExpressions.scala\n      case Acos(expressionExtractor(child))      => f(\"ACOS\", child)\n      case Asin(expressionExtractor(child))      => f(\"ASIN\", child)\n      case Atan(expressionExtractor(child))      => f(\"ATAN\", child)\n      case Ceil(expressionExtractor(child))      => f(\"CEIL\", child)\n      case Cos(expressionExtractor(child))       => f(\"COS\", child)\n      case Exp(expressionExtractor(child))       => f(\"EXP\", child)\n      case Expm1(expressionExtractor(child))     => block(func(\"EXP\", child) + \"- 1\")\n      case Floor(expressionExtractor(child))     => f(\"FLOOR\", child)\n      case Log(expressionExtractor(child))       => f(\"LOG\", child)\n      case Log2(expressionExtractor(child))      => f(\"LOG2\", child)\n      case Log10(expressionExtractor(child))     => f(\"LOG10\", child)\n      case Log1p(expressionExtractor(child))     => f(\"LOG\", child + \"+ 1\")\n      case Signum(expressionExtractor(child))    => f(\"SIGN\", child)\n      case Sin(expressionExtractor(child))       => f(\"SIN\", child)\n      case Sqrt(expressionExtractor(child))      => f(\"SQRT\", child)\n      case Tan(expressionExtractor(child))       => f(\"TAN\", child)\n      case Cot(expressionExtractor(child))       => f(\"COT\", child)\n      case ToDegrees(expressionExtractor(child)) => f(\"DEGREES\", child)\n      case ToRadians(expressionExtractor(child)) => f(\"RADIANS\", child)\n      case Bin(expressionExtractor(child))       => f(\"BIN\", child)\n      case Hex(expressionExtractor(child))       => f(\"HEX\", child)\n\n      //    case BoolAnd(expressionExtractor(arg)) => // Spark can't apply bool_and to smallint (Input to function 'bool_and' should have been boolean, but it's [smallint])\n      //    case BoolOr(expressionExtractor(arg))  => // Spark can't apply bool_or to smallint (Input to function 'bool_or' should have been boolean, but it's [smallint])\n      //    case ArrayForAll(expressionExtractor(arg), expressionExtractor(function))                    => ???\n      //    case SchemaOfCsv(expressionExtractor(child), options)                               => ???\n      //    case MapEntries(expressionExtractor(child))                                         => ???\n      //    case MapFilter(expressionExtractor(arg), expressionExtractor(function))                      => ???\n      //    case MapZipWith(expressionExtractor(left), expressionExtractor(right), expressionExtractor(function)) => ???\n      //    case CsvToStructs(schema, options, expressionExtractor(child), timeZoneId)          => ???\n      //    case StructsToCsv(options, expressionExtractor(child), timeZoneId)                  => ???\n      //    case SparkVersion()                                                        => ???\n      //    case TransformKeys(expressionExtractor(argument), expressionExtractor(function))             => ???\n      //    case TransformValues(expressionExtractor(argument), expressionExtractor(function))           => ???\n      //    case XxHash64(children, seed) => // we have 32-bit hash, but don't have 64-bit\n\n      // tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))\n      case Tanh(expressionExtractor(child)) =>\n        op(\"/\",\n           op(\"-\", f(\"EXP\", child), f(\"EXP\", f(\"-\", child))),\n           op(\"+\", f(\"EXP\", child), f(\"EXP\", f(\"-\", child))))\n\n      // sinh(x) = (exp(x) - exp(-x)) / 2\n      case Sinh(expressionExtractor(child)) =>\n        op(\"/\", op(\"-\", f(\"EXP\", child), f(\"EXP\", f(\"-\", child))), \"2\")\n\n      // cosh(x) = (exp(x) + exp(-x)) / 2\n      case Cosh(expressionExtractor(child)) =>\n        op(\"/\", op(\"+\", f(\"EXP\", child), f(\"EXP\", f(\"-\", child))), \"2\")\n\n      // asinh(x) = ln(x + sqrt(x^2 + 1))\n      case Asinh(expressionExtractor(child)) =>\n        f(\"LN\", op(\"+\", child, f(\"SQRT\", op(\"+\", f(\"POW\", child, \"2\"), \"1\"))))\n\n      // acosh(x) = ln(x + sqrt(x^2 - 1))\n      case Acosh(expressionExtractor(child)) =>\n        f(\"LN\", op(\"+\", child, f(\"SQRT\", op(\"-\", f(\"POW\", child, \"2\"), \"1\"))))\n\n      // atanh(x) = 1/2 * ln((1 + x)/(1 - x))\n      case Atanh(expressionExtractor(child)) =>\n        op(\"/\", f(\"LN\", op(\"/\", op(\"+\", \"1\", child), op(\"-\", \"1\", child))), \"2\")\n\n      case Rint(expressionExtractor(child)) => f(\"ROUND\", child, \"0\")\n\n      // TODO: case Factorial(expressionExtractor(child)) => ???\n      // TODO: case Cbrt(expressionExtractor(child))      => f(\"POW\", child, op(\"/\", \"1\", \"3\"))\n      //  We need to wait for the engine to implement precise cbrt\n\n      // nullExpressions.scala\n      case NullIf(expressionExtractor(left), expressionExtractor(right), _) =>\n        f(\"NULLIF\", left, right)\n      case Nvl(expressionExtractor(left), expressionExtractor(right), _) =>\n        f(\"COALESCE\", left, right)\n      case IsNull(expressionExtractor(child))    => block(child) + \"IS NULL\"\n      case IsNotNull(expressionExtractor(child)) => block(child) + \"IS NOT NULL\"\n\n      case Nvl2(expressionExtractor(expr1),\n                expressionExtractor(expr2),\n                expressionExtractor(expr3),\n                _) =>\n        f(\"IF\", expr1 + \"IS NOT NULL\", expr2, expr3)\n\n      // predicates.scala\n      case Not(expressionExtractor(child)) => block(Raw(\"NOT\") + child)\n      case If(expressionExtractor(predicate),\n              expressionExtractor(trueValue),\n              expressionExtractor(falseValue)) =>\n        f(\"IF\", predicate, trueValue, falseValue)\n\n      // TODO: case Randn(expressionExtractor(child)) => ???\n\n      // SortOrder.scala\n      // in SingleStore, nulls always come first when direction = ascending\n      case SortOrder(expressionExtractor(child), Ascending, NullsFirst, _) => block(child) + \"ASC\"\n      // in SingleStore, nulls always come last when direction = descending\n      case SortOrder(expressionExtractor(child), Descending, NullsLast, _) => block(child) + \"DESC\"\n\n      // stringExpressions.scala\n      case Upper(expressionExtractor(child))          => f(\"UPPER\", child)\n      case Lower(expressionExtractor(child))          => f(\"LOWER\", child)\n      case ConcatWs(expressionExtractor(Some(child))) => f(\"CONCAT_WS\", child)\n\n      case StringSpace(expressionExtractor(child)) =>\n        f(\"LPAD\", StringVar(\"\"), child, StringVar(\" \"))\n\n      case Length(expressionExtractor(child))      => f(\"CHAR_LENGTH\", child)\n      case BitLength(expressionExtractor(child))   => block(func(\"LENGTH\", child) + \"* 8\")\n      case OctetLength(expressionExtractor(child)) => f(\"LENGTH\", child)\n      case Ascii(expressionExtractor(child))       => f(\"ASCII\", child)\n      case Chr(expressionExtractor(child)) =>\n        f(\"IF\", f(\"ISNULL\", child), StringVar(null), f(\"CHAR\", child))\n\n      case versionSpecificExpressionGen(child) => child\n\n      case Uuid(_) if context.singlestoreVersionAtLeast(\"7.5.0\") => \"UUID()\"\n\n      case InitCap(expressionExtractor(child)) => f(\"INITCAP\", child)\n      // TODO: case StringReverse(expressionExtractor(child)) => ???\n      // TODO: case SoundEx(expressionExtractor(child)) => ???\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/JdbcHelpers.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{\n  Connection,\n  PreparedStatement,\n  ResultSet,\n  SQLException,\n  SQLInvalidAuthorizationSpecException,\n  Statement\n}\nimport java.util.Properties\nimport java.util.UUID.randomUUID\nimport com.singlestore.spark.SinglestoreOptions.{TableKey, TableKeyType}\nimport com.singlestore.spark.SQLGen.{SinglestoreVersion, StringVar, VariableList}\nimport org.apache.spark.sql.{Row, SaveMode}\nimport org.apache.spark.sql.catalyst.TableIdentifier\nimport org.apache.spark.sql.execution.datasources.jdbc.{JDBCOptions, JdbcUtils}\nimport org.apache.spark.sql.jdbc.{JdbcDialect, JdbcDialects}\nimport org.apache.spark.sql.types.{StringType, StructType}\n\nimport scala.util.{Failure, Success, Try}\n\ncase class SinglestorePartitionInfo(ordinal: Int, name: String, hostport: String)\n\nobject JdbcHelpers extends LazyLogging {\n  final val SINGLESTORE_CONNECT_TIMEOUT = \"10000\" // 10 seconds in ms\n\n  // register the SingleStoreDialect\n  JdbcDialects.registerDialect(SinglestoreDialect)\n\n  // Connection implicits\n  implicit class ConnectionHelpers(val conn: Connection) {\n    def withStatement[T](handle: Statement => T): T =\n      Loan(conn.createStatement()).to(handle)\n\n    def withPreparedStatement[T](query: String, handle: PreparedStatement => T): T =\n      Loan(conn.prepareStatement(query)).to(handle)\n  }\n\n  def getConnProperties(conf: SinglestoreOptions,\n                        isOnExecutor: Boolean,\n                        hostPorts: String*): Properties = {\n    val url: String = {\n      val base = s\"jdbc:singlestore:loadbalance://${hostPorts.mkString(\",\")}\"\n      conf.database match {\n        case Some(d) => s\"$base/$d\"\n        case None    => base\n      }\n    }\n\n    val sessionVariables = Seq(\n      \"sql_select_limit=18446744073709551615\",\n      \"compile_only=false\",\n      \"sql_mode='STRICT_ALL_TABLES,ONLY_FULL_GROUP_BY'\"\n    ).mkString(\",\")\n\n    val properties = new Properties()\n    properties.setProperty(\"url\", url)\n    properties.setProperty(\"driverClassName\", \"com.singlestore.jdbc.Driver\")\n    properties.setProperty(\"username\", conf.user)\n    properties.setProperty(\"password\", conf.password)\n    properties.setProperty(\n      \"connectionAttributes\",\n      s\"_connector_name:SingleStore Spark Connector,_connector_version:${BuildInfo.version},_product_version:${conf.sparkVersion}\")\n    properties.setProperty(\n      \"connectionProperties\",\n      (Map(\n        JDBCOptions.JDBC_TABLE_NAME -> \"XXX\",\n        \"zeroDateTimeBehavior\"      -> \"convertToNull\",\n        \"allowLoadLocalInfile\"      -> \"true\",\n        \"connectTimeout\"            -> SINGLESTORE_CONNECT_TIMEOUT,\n        \"sessionVariables\"          -> sessionVariables,\n        \"tinyInt1isBit\"             -> \"false\",\n        \"allowLocalInfile\"          -> \"true\"\n      ) ++ conf.jdbcExtraOptions)\n        .map(pair => pair._1 + \"=\" + pair._2)\n        .mkString(\";\")\n    )\n\n    // This property is ignored by DBCP during the connection creation\n    // It is needed, to have different connection pools for the driver and executor in a local mode\n    properties.setProperty(\"isOnSparkExecutor\", isOnExecutor.toString)\n\n    val connectionPoolOptions = if (isOnExecutor) {\n      conf.executorConnectionPoolOptions\n    } else {\n      conf.driverConnectionPoolOptions\n    }\n\n    if (connectionPoolOptions.enabled) {\n      properties.setProperty(\"maxTotal\", connectionPoolOptions.MaxOpenConns.toString)\n      properties.setProperty(\"maxIdle\", connectionPoolOptions.MaxIdleConns.toString)\n      properties.setProperty(\"maxWaitMillis\", connectionPoolOptions.MaxWaitMS.toString)\n      properties.setProperty(\"minEvictableIdleTimeMillis\",\n                             connectionPoolOptions.MinEvictableIdleTimeMs.toString)\n      properties.setProperty(\"maxConnLifetimeMillis\",\n                             connectionPoolOptions.MaxConnLifetimeMS.toString)\n      properties.setProperty(\"timeBetweenEvictionRunsMillis\",\n                             connectionPoolOptions.TimeBetweenEvictionRunsMS.toString)\n    }\n\n    properties\n  }\n\n  def getDDLConnProperties(conf: SinglestoreOptions, isOnExecutor: Boolean): Properties =\n    getConnProperties(conf, isOnExecutor, conf.ddlEndpoint)\n\n  def getDMLConnProperties(conf: SinglestoreOptions, isOnExecutor: Boolean): Properties =\n    getConnProperties(conf, isOnExecutor, conf.dmlEndpoints: _*)\n\n  def executeQuery(conn: Connection, query: String, variables: Any*): Iterator[Row] = {\n    val statement = conn.prepareStatement(query)\n    try {\n      fillStatementJdbc(statement, variables.toList)\n      if (!statement.execute()) {\n        // We don't have a ResultSet\n        // Return an empty iterator\n        Iterator[Row]()\n      } else {\n        val rs     = statement.getResultSet\n        val schema = jdbcUtilGetSchema(conn, rs, SinglestoreDialect, alwaysNullable = true)\n        JdbcUtils.resultSetToRows(rs, schema)\n      }\n    } finally {\n      statement.close()\n    }\n  }\n\n  def loadSchema(conf: SinglestoreOptions, query: String, variables: VariableList): StructType = {\n    conf.customSchema.getOrElse({\n      val conn =\n        SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n      try {\n        val statement =\n          conn.prepareStatement(SinglestoreDialect.getSchemaQuery(s\"($query) AS q\"))\n        try {\n          fillStatement(statement, variables)\n          val rs = statement.executeQuery()\n          try {\n            jdbcUtilGetSchema(conn, rs, SinglestoreDialect, alwaysNullable = true)\n          } finally {\n            rs.close()\n          }\n        } finally {\n          statement.close()\n        }\n      } finally {\n        conn.close()\n      }\n    })\n  }\n\n  private def jdbcUtilGetSchema(conn: Connection,\n                                resultSet: ResultSet,\n                                dialect: JdbcDialect,\n                                alwaysNullable: Boolean = false,\n                                isTimestampNTZ: Boolean = false): StructType = {\n    // Databricks has a different implementation of getSchema method\n    // Here we check 3 possible implementations and use existing one\n    val clazz = JdbcUtils.getClass\n\n    try {\n      // Try to find and call the method with `Connection`\n      val method = clazz.getMethod(\n        \"getSchema\",\n        classOf[Connection],\n        classOf[ResultSet],\n        classOf[JdbcDialect],\n        classOf[Boolean],\n        classOf[Boolean]\n      )\n      method\n        .invoke(JdbcUtils,\n                conn,\n                resultSet,\n                dialect,\n                alwaysNullable: java.lang.Boolean,\n                isTimestampNTZ: java.lang.Boolean)\n        .asInstanceOf[StructType]\n    } catch {\n      case _: NoSuchMethodException =>\n        try {\n          // Fallback to method without `Connection`\n          val method = clazz.getMethod(\n            \"getSchema\",\n            classOf[ResultSet],\n            classOf[JdbcDialect],\n            classOf[Boolean],\n            classOf[Boolean]\n          )\n          method\n            .invoke(JdbcUtils,\n                    resultSet,\n                    dialect,\n                    alwaysNullable: java.lang.Boolean,\n                    isTimestampNTZ: java.lang.Boolean)\n            .asInstanceOf[StructType]\n        } catch {\n          case _: NoSuchMethodException =>\n            // Fallback to method without `isTimestampNTZ`\n            val method = clazz.getMethod(\n              \"getSchema\",\n              classOf[ResultSet],\n              classOf[JdbcDialect],\n              classOf[Boolean]\n            )\n            method\n              .invoke(JdbcUtils, resultSet, dialect, alwaysNullable: java.lang.Boolean)\n              .asInstanceOf[StructType]\n        }\n    }\n  }\n\n  def explainQuery(conf: SinglestoreOptions, query: String, variables: VariableList): String = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n    try {\n      val statement = conn.prepareStatement(s\"EXPLAIN $query\")\n      try {\n        fillStatement(statement, variables)\n        val rs = statement.executeQuery()\n        try {\n          var out = List.empty[String]\n          while (rs.next) {\n            out = rs.getString(1) :: out\n          }\n          out.reverseIterator.mkString(\"\\n\")\n        } finally {\n          rs.close()\n        }\n      } finally {\n        statement.close()\n      }\n    } finally {\n      conn.close()\n    }\n  }\n\n  // explainJSONQuery runs `EXPLAIN JSON` on the query and returns the String\n  // representing this queries plan as JSON.\n  def explainJSONQuery(conf: SinglestoreOptions, query: String, variables: VariableList): String = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n    try {\n      val statement = conn.prepareStatement(s\"EXPLAIN JSON ${query}\")\n      try {\n        fillStatement(statement, variables)\n        val rs = statement.executeQuery()\n        try {\n          // we only expect one row in the output\n          if (!rs.next()) { assert(false, \"EXPLAIN JSON failed to return a row\") }\n          val json = rs.getString(1)\n          assert(!rs.next(), \"EXPLAIN JSON returned more than one row\")\n          json\n        } finally {\n          rs.close()\n        }\n      } finally {\n        statement.close()\n      }\n    } finally {\n      conn.close()\n    }\n  }\n\n  // partitionHostPorts returns a list of (ordinal, name, host:port) for all master\n  // partitions in the specified database\n  def partitionHostPorts(conf: SinglestoreOptions,\n                         database: String): List[SinglestorePartitionInfo] = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n    try {\n      val statement = conn.prepareStatement(s\"\"\"\n        SELECT HOST, PORT\n        FROM INFORMATION_SCHEMA.DISTRIBUTED_PARTITIONS\n        WHERE DATABASE_NAME = ? AND ROLE = \"Master\"\n        ORDER BY ORDINAL ASC\n      \"\"\")\n      try {\n        fillStatement(statement, List(StringVar(database)))\n        val rs = statement.executeQuery()\n        try {\n          var out = List.empty[SinglestorePartitionInfo]\n          var idx = 0\n          while (rs.next) {\n            out = SinglestorePartitionInfo(idx,\n                                           s\"${database}_${idx}\",\n                                           s\"${rs.getString(1)}:${rs.getInt(2)}\") :: out\n            idx += 1\n          }\n          out.reverse\n        } finally {\n          rs.close()\n        }\n      } finally {\n        statement.close()\n      }\n    } finally {\n      conn.close()\n    }\n  }\n\n  /**\n    * Return map from original host/port to external host/port\n    * @param conf options\n    * @return Map `host:port` -> `externalHost:externalPort`\n    */\n  def externalHostPorts(conf: SinglestoreOptions): Map[String, String] = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n    try {\n      val statement = conn.prepareStatement(s\"\"\"\n        SELECT IP_ADDR,    \n        PORT,\n        EXTERNAL_HOST,         \n        EXTERNAL_PORT\n        FROM INFORMATION_SCHEMA.MV_NODES \n        WHERE TYPE = \"LEAF\";\n      \"\"\")\n      try {\n        val rs = statement.executeQuery()\n        try {\n          var out = Map.empty[String, String]\n          while (rs.next) {\n            val host               = rs.getString(1)\n            val port               = rs.getInt(2)\n            val externalHost       = rs.getString(3)\n            val externalPortString = rs.getString(4)\n            if (externalHost != null && externalPortString != null) {\n              val externalPort = externalPortString.toInt\n              out = out + (s\"$host:$port\" -> s\"$externalHost:$externalPort\")\n            }\n          }\n          out\n        } finally {\n          rs.close()\n        }\n      } finally {\n        statement.close()\n      }\n    } finally {\n      conn.close()\n    }\n  }\n\n  def fillStatement(stmt: PreparedStatement, variables: VariableList): Unit = {\n    import SQLGen._\n    if (variables.isEmpty) { return }\n\n    variables.zipWithIndex.foreach {\n      case (StringVar(v), index) => stmt.setString(index + 1, v)\n      case (IntVar(v), index)    => stmt.setInt(index + 1, v)\n      case (LongVar(v), index)   => stmt.setLong(index + 1, v)\n      case (ShortVar(v), index)  => stmt.setShort(index + 1, v)\n      case (FloatVar(v), index)  => stmt.setFloat(index + 1, v)\n      case (DoubleVar(v), index) => stmt.setDouble(index + 1, v)\n      case (DecimalVar(v), index) =>\n        stmt.setBigDecimal(index + 1, v.toJavaBigDecimal)\n      case (BooleanVar(v), index)   => stmt.setBoolean(index + 1, v)\n      case (ByteVar(v), index)      => stmt.setByte(index + 1, v)\n      case (DateVar(v), index)      => stmt.setDate(index + 1, v)\n      case (TimestampVar(v), index) => stmt.setTimestamp(index + 1, v)\n      case (v, _) =>\n        throw new IllegalArgumentException(\n          \"Unexpected Variable Type: \" + v.getClass.getName\n        )\n    }\n  }\n\n  def fillStatementJdbc(stmt: PreparedStatement, variables: List[Any]): Unit = {\n    // here we leave it to JDBC driver to do type conversions\n    if (variables.isEmpty) { return }\n    for ((v, index) <- variables.zipWithIndex) {\n      stmt.setObject(index + 1, v)\n    }\n  }\n\n  def schemaToString(schema: StructType,\n                     tableKeys: List[TableKey],\n                     createRowstoreTable: Boolean): String = {\n    // spark should never call any of our code if the schema is empty\n    assert(schema.length > 0)\n\n    val fieldsSql = schema.fields\n      .map(field => {\n        val name = SinglestoreDialect.quoteIdentifier(field.name)\n        val typ = SinglestoreDialect\n          .getJDBCType(field.dataType)\n          .getOrElse(\n            throw new IllegalArgumentException(\n              s\"Can't get JDBC type for ${field.dataType.simpleString}\"\n            )\n          )\n        val nullable  = if (field.nullable) \"\" else \" NOT NULL\"\n        val collation = if (field.dataType == StringType) \" COLLATE UTF8_BIN\" else \"\"\n        s\"${name} ${typ.databaseTypeDefinition}${collation}${nullable}\"\n      })\n\n    // we want to default all tables to columnstore, but in 6.8 and below you *must*\n    // specify a sort key so we just pick the first column arbitrarily for now\n    var finalTableKeys = tableKeys\n    // if all the keys are shard keys it means there are no other keys so we can default\n    if (!createRowstoreTable && tableKeys.forall(_.keyType == TableKeyType.Shard)) {\n      finalTableKeys = TableKey(TableKeyType.Columnstore, columns = schema.head.name) :: tableKeys\n    }\n\n    def keyNameColumnsSQL(key: TableKey) =\n      s\"${key.name.map(SinglestoreDialect.quoteIdentifier).getOrElse(\"\")}(${key.columns})\"\n\n    val keysSql = finalTableKeys.map {\n      case key @ TableKey(TableKeyType.Primary, _, _) => s\"PRIMARY KEY ${keyNameColumnsSQL(key)}\"\n      case key @ TableKey(TableKeyType.Columnstore, _, _) =>\n        s\"KEY ${keyNameColumnsSQL(key)} USING CLUSTERED COLUMNSTORE\"\n      case key @ TableKey(TableKeyType.Unique, _, _) => s\"UNIQUE KEY ${keyNameColumnsSQL(key)}\"\n      case key @ TableKey(TableKeyType.Shard, _, _)  => s\"SHARD KEY ${keyNameColumnsSQL(key)}\"\n      case key @ TableKey(TableKeyType.Key, _, _)    => s\"KEY ${keyNameColumnsSQL(key)}\"\n    }\n\n    (fieldsSql ++ keysSql).mkString(\"(\\n  \", \",\\n  \", \"\\n)\")\n  }\n\n  def tableExists(conn: Connection, table: TableIdentifier): Boolean = {\n    conn.withStatement(\n      stmt =>\n        Try {\n          try {\n            stmt.execute(SinglestoreDialect.getTableExistsQuery(table.quotedString))\n          } finally {\n            stmt.close()\n          }\n        }.isSuccess\n    )\n  }\n\n  def getSinglestoreVersion(conf: SinglestoreOptions): String = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n    val sql = \"select @@memsql_version\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n    val resultSet = conn.withStatement(stmt => {\n      try {\n        stmt.executeQuery(sql)\n      } catch {\n        case _: SQLException => throw new IllegalArgumentException(\"Can't get SingleStore version\")\n      } finally {\n        stmt.close()\n        conn.close()\n      }\n    })\n    if (resultSet.next()) {\n      resultSet.getString(\"@@memsql_version\")\n    } else throw new IllegalArgumentException(\"Can't get SingleStore version\")\n  }\n\n  def createTable(conn: Connection,\n                  table: TableIdentifier,\n                  schema: StructType,\n                  tableKeys: List[TableKey],\n                  createRowstoreTable: Boolean,\n                  version: SinglestoreVersion): Unit = {\n    val sql =\n      s\"CREATE ${if (createRowstoreTable && version.atLeast(\"7.3.0\")) \"ROWSTORE\" else \"\"} TABLE ${table.quotedString} ${schemaToString(schema, tableKeys, createRowstoreTable)}\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n    conn.withStatement(stmt => stmt.executeUpdate(sql))\n  }\n\n  def getPartitionsCount(conn: Connection, database: String): Int = {\n    val sql =\n      s\"SELECT num_partitions FROM information_schema.DISTRIBUTED_DATABASES WHERE database_name = '$database'\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n    val resultSet = conn.withStatement(stmt => stmt.executeQuery(sql))\n\n    if (resultSet.next()) {\n      resultSet.getInt(\"num_partitions\")\n    } else {\n      throw new IllegalArgumentException(\n        s\"Failed to get number of partitions for '$database' database\")\n    }\n  }\n\n  def getResultTableName(applicationId: String,\n                         stageId: Int,\n                         rddId: Int,\n                         attemptNumber: Int,\n                         randHex: String): String = {\n    s\"rt_${applicationId.replace(\"-\", \"\")}_${stageId}_${rddId}_${attemptNumber}_${randHex}\"\n  }\n\n  def getCreateResultTableQuery(tableName: String,\n                                query: String,\n                                schema: StructType,\n                                materialized: Boolean,\n                                needsRepartition: Boolean,\n                                repartitionColumns: Seq[String]): String = {\n    val materializedStr = { if (materialized) { \"MATERIALIZED\" } else \"\" }\n    if (needsRepartition) {\n      if (repartitionColumns.isEmpty) {\n        val randColName = s\"randColumn${randomUUID().toString.replace(\"-\", \"\")}\"\n        s\"CREATE $materializedStr RESULT TABLE $tableName PARTITION BY ($randColName) AS SELECT *, RAND() AS $randColName FROM ($query)\"\n      } else {\n        s\"CREATE $materializedStr RESULT TABLE $tableName PARTITION BY (${repartitionColumns\n          .mkString(\",\")}) AS SELECT * FROM ($query)\"\n      }\n    } else {\n      s\"CREATE $materializedStr RESULT TABLE $tableName AS $query\"\n    }\n  }\n\n  def getSelectFromResultTableQuery(tableName: String, partition: Int): String = {\n    s\"SELECT * FROM ::$tableName WHERE partition_id() = $partition\"\n  }\n\n  def createResultTable(conn: Connection,\n                        tableName: String,\n                        query: String,\n                        schema: StructType,\n                        variables: VariableList,\n                        materialized: Boolean,\n                        needsRepartition: Boolean,\n                        repartitionColumns: Seq[String]): Unit = {\n    val sql =\n      getCreateResultTableQuery(tableName,\n                                query,\n                                schema,\n                                materialized,\n                                needsRepartition,\n                                repartitionColumns)\n    log.trace(s\"Executing SQL:\\n$sql\")\n\n    conn.withPreparedStatement(sql, stmt => {\n      JdbcHelpers.fillStatement(stmt, variables)\n      stmt.executeUpdate()\n    })\n  }\n\n  def dropResultTable(conn: Connection, tableName: String): Unit = {\n    val sql = s\"DROP RESULT TABLE $tableName\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n\n    conn.withStatement(stmt => {\n      stmt.executeUpdate(sql)\n    })\n  }\n\n  def isValidQuery(conn: Connection, query: String, variables: VariableList): Boolean = {\n    val sql = s\"EXPLAIN $query\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n\n    Try {\n      conn.withPreparedStatement(sql, stmt => {\n        JdbcHelpers.fillStatement(stmt, variables)\n        stmt.execute()\n      })\n    } match {\n      case Success(_) => true\n      // SQLInvalidAuthorizationSpecException is thrown when password (or token in the case of JWT authentification) is wrong\n      case Failure(e: SQLException)\n          if !e.isInstanceOf[SQLInvalidAuthorizationSpecException] &&\n            // Original exception can be wrapped inside of the connection pool\n            !(e.getCause != null && e.getCause\n              .isInstanceOf[SQLInvalidAuthorizationSpecException]) =>\n        false\n    }\n  }\n\n  def truncateTable(conn: Connection, table: TableIdentifier): Unit = {\n    val sql = s\"TRUNCATE TABLE ${table.quotedString}\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n    conn.withStatement(stmt => stmt.executeUpdate(sql))\n  }\n\n  def dropTable(conn: Connection, table: TableIdentifier): Unit = {\n    val sql = s\"DROP TABLE ${table.quotedString}\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n    conn.withStatement(stmt => stmt.executeUpdate(sql))\n  }\n\n  def isReferenceTable(conf: SinglestoreOptions, table: TableIdentifier): Boolean = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n    // Assume that either table.database is set or conf.database is set\n    val databaseName =\n      table.database\n        .orElse(conf.database)\n        .getOrElse(throw new IllegalArgumentException(\"Database name should be defined\"))\n    val sql = s\"using $databaseName show tables extended like '${table.table}'\"\n    log.trace(s\"Executing SQL:\\n$sql\")\n    val resultSet = conn.withStatement(stmt => {\n      Try {\n        try {\n          stmt.executeQuery(sql)\n        } finally {\n          stmt.close()\n          conn.close()\n        }\n      }\n    })\n    resultSet.toOption.fold(false)(resultSet => {\n      if (resultSet.next()) {\n        !resultSet.getBoolean(\"distributed\")\n      } else {\n        throw new IllegalArgumentException(s\"Table `$databaseName.${table.table}` doesn't exist\")\n      }\n    })\n  }\n\n  def prepareTableForWrite(conf: SinglestoreOptions,\n                           table: TableIdentifier,\n                           mode: SaveMode,\n                           schema: StructType): Unit = {\n    val version = SinglestoreVersion(JdbcHelpers.getSinglestoreVersion(conf))\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n    try {\n      if (JdbcHelpers.tableExists(conn, table)) {\n        mode match {\n          case SaveMode.Overwrite =>\n            conf.overwriteBehavior match {\n              case Truncate =>\n                JdbcHelpers.truncateTable(conn, table)\n              case DropAndCreate =>\n                JdbcHelpers.dropTable(conn, table)\n                JdbcHelpers.createTable(conn,\n                                        table,\n                                        schema,\n                                        conf.tableKeys,\n                                        conf.createRowstoreTable,\n                                        version)\n              case Merge =>\n              // nothing to do\n            }\n          case SaveMode.ErrorIfExists =>\n            sys.error(\n              s\"Table '${table}' already exists. SaveMode: ErrorIfExists.\"\n            )\n          case SaveMode.Ignore =>\n          // table already exists, nothing to do\n          case SaveMode.Append => // continue\n        }\n      } else {\n        JdbcHelpers.createTable(conn,\n                                table,\n                                schema,\n                                conf.tableKeys,\n                                conf.createRowstoreTable,\n                                version)\n      }\n    } finally {\n      conn.close()\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/LazyLogging.scala",
    "content": "package com.singlestore.spark\n\nimport org.slf4j.{Logger, LoggerFactory}\n\ntrait LazyLogging {\n  @transient\n  protected lazy val log: Logger = LoggerFactory.getLogger(getClass.getName)\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/Loan.scala",
    "content": "package com.singlestore.spark\n\nclass Loan[A <: AutoCloseable](resource: A) {\n  def to[T](handle: A => T): T =\n    try handle(resource)\n    finally resource.close()\n}\n\nobject Loan {\n  def apply[A <: AutoCloseable](resource: A) = new Loan(resource)\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/MetricsHandler.scala",
    "content": "package org.apache.spark.metrics.source\n\nimport org.apache.spark.TaskContext\n\nobject MetricsHandler {\n  def setRecordsWritten(r: Long): Unit = {\n    TaskContext.get().taskMetrics().outputMetrics.setRecordsWritten(r)\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/OverwriteBehavior.scala",
    "content": "package com.singlestore.spark\n\nsealed trait OverwriteBehavior\n\ncase object Truncate      extends OverwriteBehavior\ncase object Merge         extends OverwriteBehavior\ncase object DropAndCreate extends OverwriteBehavior\n\nobject OverwriteBehavior {\n  def apply(value: String): OverwriteBehavior = value.toLowerCase match {\n    case \"truncate\"      => Truncate\n    case \"merge\"         => Merge\n    case \"dropandcreate\" => DropAndCreate\n    case _ =>\n      throw new IllegalArgumentException(\n        s\"Illegal argument for `${SinglestoreOptions.OVERWRITE_BEHAVIOR}` option\")\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/ParallelReadEnablement.scala",
    "content": "package com.singlestore.spark\n\nsealed trait ParallelReadEnablement\n\ncase object Disabled      extends ParallelReadEnablement\ncase object Automatic     extends ParallelReadEnablement\ncase object AutomaticLite extends ParallelReadEnablement\ncase object Forced        extends ParallelReadEnablement\n\nobject ParallelReadEnablement {\n  def apply(value: String): ParallelReadEnablement = value.toLowerCase match {\n    case \"disabled\"      => Disabled\n    case \"automaticlite\" => AutomaticLite\n    case \"automatic\"     => Automatic\n    case \"forced\"        => Forced\n\n    // These two options are added for compatibility purposes\n    case \"false\" => Disabled\n    case \"true\"  => Automatic\n\n    case _ =>\n      throw new IllegalArgumentException(\n        s\"\"\"Illegal argument for `${SinglestoreOptions.ENABLE_PARALLEL_READ}` option. Valid arguments are:\n           | - \"Disabled\"\n           | - \"AutomaticLite\"\n           | - \"Automatic\"\n           | - \"Forced\"\"\"\".stripMargin)\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/ParallelReadType.scala",
    "content": "package com.singlestore.spark\n\nsealed trait ParallelReadType\n\ncase object ReadFromLeaves                  extends ParallelReadType\ncase object ReadFromAggregators             extends ParallelReadType\ncase object ReadFromAggregatorsMaterialized extends ParallelReadType\n\nobject ParallelReadType {\n  def apply(value: String): ParallelReadType = value.toLowerCase match {\n    case \"readfromleaves\"                  => ReadFromLeaves\n    case \"readfromaggregators\"             => ReadFromAggregators\n    case \"readfromaggregatorsmaterialized\" => ReadFromAggregatorsMaterialized\n    case _ =>\n      throw new IllegalArgumentException(\n        s\"\"\"Illegal argument for `${SinglestoreOptions.PARALLEL_READ_FEATURES}` option. Valid arguments are:\n           | - \"ReadFromLeaves\"\n           | - \"ReadFromAggregators\"\n           | - \"ReadFromAggregatorsMaterialized\"\"\"\".stripMargin)\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SQLGen.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Date, Timestamp}\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.catalyst.plans._\nimport org.apache.spark.sql.catalyst.plans.logical._\nimport org.apache.spark.sql.types._\nimport org.slf4j.{Logger, LoggerFactory}\nimport com.singlestore.spark.JdbcHelpers.getDMLConnProperties\nimport org.apache.spark.sql.execution.datasources.LogicalRelation\n\nimport scala.collection.immutable.HashMap\nimport scala.collection.mutable\n\nobject SQLGen extends LazyLogging {\n  type VariableList = List[Var[_]]\n\n  trait Joinable {\n    def +(j: Joinable): Statement\n    def +(s: String): Statement = this + Raw(s)\n  }\n\n  trait Chunk extends Joinable {\n    def +(j: Joinable): Statement = j match {\n      case Statement(list, output) => Statement(list ::: this :: Nil, output)\n      case c: Chunk                => Statement(c :: this :: Nil)\n    }\n\n    def toSQL(fieldMap: Map[ExprId, Attribute]): String\n  }\n\n  case class Statement(list: List[Chunk], output: Seq[AttributeReference] = Nil)\n      extends Joinable\n      with LazyLogging {\n\n    lazy val reverseList: List[Chunk] = list.reverse\n\n    lazy val relations: Seq[Relation] = reverseList.collect {\n      case r: Relation => r\n    }\n\n    lazy val fieldMap: Map[ExprId, Attribute] = relations\n      .flatMap(_.output)\n      .map(a => (a.exprId, a))\n      .toMap\n\n    lazy val variables: VariableList =\n      reverseList.collect {\n        case r: Relation => r.reader.variables\n        case r: Var[_]   => Iterator(r)\n      }.flatten\n\n    lazy val sql: String = reverseList.map(_.toSQL(fieldMap)).mkString(\" \")\n\n    def asLogicalPlan(isFinal: Boolean = false): LogicalPlan =\n      relations.head.toLogicalPlan(output, sql, variables, isFinal, relations.head.reader.context)\n\n    private def newlineIfEmpty: String = list match {\n      case Nil => \"\"\n      case _   => \"\\n\"\n    }\n\n    // ------------------------------------\n    // Builder functions for easy chaining\n    // ------------------------------------\n\n    def +(j: Joinable): Statement = j match {\n      case Statement(otherList, _) => copy(otherList ::: list)\n      case c: Chunk                => copy(c :: list)\n    }\n\n    private val MAX_PLAN_FIELDS: Int = Int.MaxValue\n    def withLogicalPlanComment(plan: LogicalPlan): Statement =\n      if (log.isTraceEnabled()) {\n        this + s\"${newlineIfEmpty}-- Spark LogicalPlan: ${plan.simpleString(MAX_PLAN_FIELDS).replace(\"\\n\", \"\\n-- \")}\"\n      } else {\n        this\n      }\n\n    def selectAll(): Statement                 = this + \"\\nSELECT *\"\n    def select(c: Joinable): Statement         = this + \"\\nSELECT\" + c\n    def select(c: Option[Joinable]): Statement = this + \"\\nSELECT\" + c.getOrElse(Raw(\"*\"))\n\n    def from(c: Joinable): Statement = this + \"\\nFROM\" + c\n\n    def join(c: Joinable, joinType: JoinType): Statement =\n      joinType match {\n        case Inner                   => this + \"\\nINNER JOIN\" + c\n        case Cross                   => this + \"\\nCROSS JOIN\" + c\n        case LeftOuter               => this + \"\\nLEFT OUTER JOIN\" + c\n        case RightOuter              => this + \"\\nRIGHT OUTER JOIN\" + c\n        case FullOuter               => this + \"\\nFULL OUTER JOIN\" + c\n        case NaturalJoin(Inner)      => this + \"\\nNATURAL JOIN\" + c\n        case NaturalJoin(LeftOuter)  => this + \"\\nNATURAL LEFT OUTER JOIN\" + c\n        case NaturalJoin(RightOuter) => this + \"\\nNATURAL RIGHT OUTER JOIN\" + c\n        case NaturalJoin(FullOuter)  => this + \"\\nNATURAL FULL OUTER JOIN\" + c\n        case _                       => throw new IllegalArgumentException(s\"join type $joinType not supported\")\n      }\n\n    def on(c: Joinable): Statement         = this + \"ON\" + c\n    def on(c: Option[Joinable]): Statement = c.map(on).getOrElse(this)\n\n    def where(c: Joinable): Statement = this + \"\\nWHERE\" + c\n\n    def groupby(c: Joinable): Statement         = this + \"\\nGROUP BY\" + c\n    def groupby(c: Option[Joinable]): Statement = c.map(groupby).getOrElse(this)\n\n    def limit(c: Joinable): Statement = this + \"\\nLIMIT\" + c\n\n    def orderby(c: Joinable): Statement         = this + \"\\nORDER BY\" + c\n    def orderby(c: Option[Joinable]): Statement = c.map(orderby).getOrElse(this)\n\n    def output(o: Seq[Attribute], updateFromFieldMap: Boolean = true): Statement =\n      copy(\n        output = o.map(\n          f => {\n            val target = if (updateFromFieldMap) fieldMap.getOrElse(f.exprId, f) else f\n            AttributeReference(target.name, f.dataType, f.nullable, f.metadata)(\n              f.exprId,\n              f.qualifier\n            )\n          }\n        )\n      )\n  }\n\n  // ----------------------------------\n  // Primary Chunk Types\n  // ----------------------------------\n\n  trait SQLChunk extends Chunk {\n    val sql: String\n    override def toSQL(fieldMap: Map[ExprId, Attribute]): String = sql\n  }\n\n  case class Raw(override val sql: String) extends SQLChunk\n\n  case class Ident(name: String) extends SQLChunk {\n    override val sql: String = SinglestoreDialect.quoteIdentifier(name)\n\n    // it's not clear that we ever need to fully-qualify references since we do field renames with expr-ids\n    // If this changes then you can change this code to something like this:\n    // (and grab the qualifier when creating Ident)\n    //      qualifier\n    //        .map(q => s\"${SinglestoreDialect.quoteIdentifier(q)}.\")\n    //        .getOrElse(\"\") + SinglestoreDialect.quoteIdentifier(name)\n  }\n\n  case class Relation(\n      rawOutput: Seq[Attribute],\n      reader: SinglestoreReader,\n      name: String,\n      toLogicalPlan: (Seq[AttributeReference],\n                      String,\n                      VariableList,\n                      Boolean,\n                      SQLGenContext) => LogicalPlan\n  ) extends SQLChunk {\n\n    val isFinal = reader.isFinal\n\n    val output = rawOutput.map(\n      a => AttributeReference(a.name, a.dataType, a.nullable, a.metadata)(a.exprId)\n    )\n\n    override val sql: String = {\n      var inAttributeName: Boolean = false\n\n      // Add indentation after new line character if it is not in the attribute name.\n      // We are inside of the Attribute if the number of backticks we already processed is odd.\n      // Example:\n      // \"select id as `name\\n\\n``name` from \\n table\" -> \"select id as `name\\n\\n``name` from \\n   table\"\n      val indentedQuery = reader.query\n        .map({\n          case '`' =>\n            inAttributeName = !inAttributeName\n            \"`\"\n          case '\\n' =>\n            if (inAttributeName) {\n              \"\\n\"\n            } else {\n              \"\\n  \"\n            }\n          case c => c.toString\n        })\n        .mkString\n\n      val alias = SinglestoreDialect.quoteIdentifier(name)\n      s\"(\\n  $indentedQuery\\n) AS $alias\"\n    }\n\n    def renameOutput: LogicalPlan =\n      select(\n        output\n          .map(a =>\n            alias(SinglestoreDialect.quoteIdentifier(a.name), a.name, a.exprId, reader.context))\n          .reduce(_ + \",\" + _))\n        .from(this)\n        .output(output)\n        .asLogicalPlan()\n\n    def castOutputAndFinalize: LogicalPlan = {\n      val schema = try {\n        reader.schema\n      } catch {\n        case e: Exception => {\n          log.error(s\"Failed to compute schema for reader:\\n${reader.toString}\")\n          throw e\n        }\n      }\n\n      val castedOutputExpr = output\n        .zip(schema)\n        .map({\n          case (a, f) if a.dataType != f.dataType =>\n            Alias(Cast(a, a.dataType), a.name)(a.exprId, a.qualifier, Some(a.metadata))\n\n          case (a, _) => a\n        })\n      val expressionExtractor = ExpressionExtractor(reader.context)\n\n      select(castedOutputExpr match {\n        case expressionExtractor(expr) => expr\n        case _                         => None\n      }).from(this)\n        .output(output)\n        .asLogicalPlan(true)\n    }\n  }\n\n  object Relation {\n    def unapply(source: LogicalPlan): Option[Relation] = {\n      source match {\n        case lr: LogicalRelation if lr.relation.isInstanceOf[SinglestoreReader] => {\n          val reader = lr.relation.asInstanceOf[SinglestoreReader]\n          def convertBack(output: Seq[AttributeReference],\n                          sql: String,\n                          variables: VariableList,\n                          isFinal: Boolean,\n                          context: SQLGenContext): LogicalPlan = {\n            LogicalRelationCompat.copyWithReader(lr,\n                                                 reader.copy(query = sql,\n                                                             variables = variables,\n                                                             isFinal = isFinal,\n                                                             expectedOutput = output,\n                                                             context = context))\n          }\n\n          Some(Relation(lr.output, reader, reader.context.nextAlias(), convertBack))\n        }\n        case _ => None\n      }\n    }\n  }\n\n  // In some versions of Spark, LogicalRelation has an extra `stream`\n  // argument, while in others it does not. This extractor abstracts away the differences\n  // and safely retrieves the common fields. It will also extract the `stream` attribute only if it exists\n  // in the current runtime class, avoiding compile-time errors and runtime crashes.\n  object LogicalRelationCompat {\n    def copyWithReader(lr: LogicalRelation, reader: SinglestoreReader): LogicalRelation = {\n      val cls = lr.getClass\n      val copies = cls.getMethods.iterator\n        .filter(m => m.getName == \"copy\")\n        .toList\n        .sortBy(_.getParameterCount)\n\n      // pick the most specific/longest copy; we’ll fill defaults for trailing params\n      val copyM = copies.lastOption.getOrElse {\n        throw new NoSuchMethodError(\"LogicalRelation.copy not found.\")\n      }\n\n      // The first four params are stable across versions:\n      // relation: BaseRelation\n      // output: Seq[AttributeReference]\n      // catalogTable: Option[CatalogTable]\n      // isStreaming: Boolean\n      val baseArgs: Array[AnyRef] = Array(\n        reader.asInstanceOf[AnyRef],\n        reader.expectedOutput,\n        lr.catalogTable.asInstanceOf[AnyRef],\n        java.lang.Boolean.valueOf(lr.isStreaming)\n      )\n\n      val paramCount = copyM.getParameterCount\n      val args =\n        if (paramCount <= 4) baseArgs\n        else {\n          // Fill in extra params with their default values: copy$default$5, copy$default$6, ...\n          val extras = (5 to paramCount).map { i =>\n            val dm = cls.getMethod(s\"copy$$default$$$i\")\n            dm.invoke(lr)\n          }\n          baseArgs ++ extras\n        }\n\n      copyM.invoke(lr, args: _*).asInstanceOf[LogicalRelation]\n    }\n  }\n\n  case class Attr(a: Attribute, context: SQLGenContext) extends Chunk {\n    override def toSQL(fieldMap: Map[ExprId, Attribute]): String = {\n      val target = fieldMap.getOrElse(a.exprId, a)\n      context.ident(target.name, target.exprId)\n    }\n  }\n\n  // ----------------------------------\n  // Variables\n  // ----------------------------------\n\n  sealed trait Var[T] extends SQLChunk {\n    override val sql: String = \"?\"\n    val variable: T\n  }\n  case class StringVar(override val variable: String)       extends Var[String]\n  case class IntVar(override val variable: Int)             extends Var[Int]\n  case class LongVar(override val variable: Long)           extends Var[Long]\n  case class ShortVar(override val variable: Short)         extends Var[Short]\n  case class FloatVar(override val variable: Float)         extends Var[Float]\n  case class DoubleVar(override val variable: Double)       extends Var[Double]\n  case class DecimalVar(override val variable: Decimal)     extends Var[Decimal]\n  case class BooleanVar(override val variable: Boolean)     extends Var[Boolean]\n  case class ByteVar(override val variable: Byte)           extends Var[Byte]\n  case class DateVar(override val variable: Date)           extends Var[Date]\n  case class TimestampVar(override val variable: Timestamp) extends Var[Timestamp]\n\n  // ----------------------------------\n  // Builder functions and constants\n  // ----------------------------------\n\n  final val empty: Statement = Statement(Nil)\n\n  implicit def stringToJoinable(s: String): Joinable = Raw(s)\n\n  def block(j: Joinable): Statement = Raw(\"(\") + j + \")\"\n\n  def alias(j: Joinable, n: String, e: ExprId, context: SQLGenContext): Statement =\n    block(j) + \"AS\" + context.ident(n, e)\n\n  def func(n: String, j: Joinable): Statement  = Raw(n) + block(j)\n  def func(n: String, j: Joinable*): Statement = Raw(n) + block(j.reduce(_ + \",\" + _))\n\n  def cast(j: Joinable, t: Joinable): Statement = func(\"CONVERT\", j, t)\n\n  def newStatement(sourcePlan: LogicalPlan): Statement = empty.withLogicalPlanComment(sourcePlan)\n\n  def selectAll: Statement                   = Statement(Raw(\"SELECT *\") :: Nil)\n  def select(c: Joinable): Statement         = Raw(\"SELECT\") + c\n  def select(c: Option[Joinable]): Statement = Raw(\"SELECT\") + c.getOrElse(Raw(\"*\"))\n\n  def sqlMapValueCaseInsensitive(value: Joinable,\n                                 mappings: Map[String, String],\n                                 default: Joinable): Joinable =\n    value match {\n      case StringVar(s) => mappings.get(s.toLowerCase).map(StringVar).getOrElse(default)\n      case _ =>\n        block(mappings.foldLeft(Raw(\"CASE\") + func(\"LOWER\", value))({\n          case (memo, (key, value)) =>\n            memo + \"WHEN\" + StringVar(key.toLowerCase) + \"THEN\" + StringVar(value.toLowerCase)\n        }) + \"ELSE\" + default + \"END\")\n    }\n\n  case class SortPredicates(expressionExtractor: ExpressionExtractor) {\n    def joinPredicates(predicates: Seq[Option[Joinable]], operation: String): Option[Joinable] = {\n      predicates\n        .sortWith((p1, p2) => p1.toString < p2.toString)\n        .reduce[Option[Joinable]] {\n          case (Some(left), Some(right)) => Some(ExpressionGen.op(operation, left, right))\n          case _                         => None\n        }\n    }\n\n    def extractOr(expr: Expression): Seq[Option[Joinable]] = expr match {\n      case Or(left, right)           => extractOr(left) ++ extractOr(right)\n      case And(left, right)          => Seq(joinPredicates(extractAnd(left) ++ extractAnd(right), \"AND\"))\n      case expressionExtractor(expr) => Seq(Some(expr))\n      case _                         => Seq(None)\n    }\n\n    def extractAnd(expr: Expression): Seq[Option[Joinable]] = expr match {\n      case And(left, right)          => extractAnd(left) ++ extractAnd(right)\n      case Or(left, right)           => Seq(joinPredicates(extractOr(left) ++ extractOr(right), \"OR\"))\n      case expressionExtractor(expr) => Seq(Some(expr))\n      case _                         => Seq(None)\n    }\n\n    def unapply(expr: Expression): Option[Joinable] = {\n      joinPredicates(extractAnd(expr), \"AND\")\n    }\n\n    // None -> Some(None) nothing to compile results in no SQL\n    // Some(good expr) -> Some(Some(sql)) we can compile, results in SQL\n    // Some(bad expr) -> None failed to compile, unapply does not match\n    def unapply(expr: Option[Expression]): Option[Option[Joinable]] = expr match {\n      case None             => Some(None)\n      case Some(expression) => joinPredicates(extractAnd(expression), \"AND\").map(j => Some(j))\n    }\n  }\n\n  case class StatementWithOrder(expressionExtractor: ExpressionExtractor) {\n    def unapply(source: LogicalPlan): Option[(LogicalPlan, Seq[SortOrder])] = {\n      val limitWithOrder = LimitWithOrder(expressionExtractor)\n\n      source match {\n        case plan @ VersionSpecificSortExtractor(order @ expressionExtractor(expr),\n                                                 true,\n                                                 Relation(relation)) =>\n          Some(\n            newStatement(plan)\n              .selectAll()\n              .from(relation)\n              .orderby(expr)\n              // For now - we add a huge limit to all sort queries which forces SingleStore to preserve the order by.\n              // fromTopLevelSort and fromLimit will handle pushing down sort without a max-int limit.\n              .limit(Long.MaxValue.toString)\n              .output(plan.output)\n              .asLogicalPlan(),\n            order\n          )\n\n        case limitWithOrder(logicalPlan, order) => Some(logicalPlan, order)\n\n        case _ => None\n      }\n    }\n  }\n\n  case class LimitWithOrder(expressionExtractor: ExpressionExtractor) {\n    def unapply(source: LogicalPlan): Option[(LogicalPlan, Seq[SortOrder])] = {\n      source match {\n        case plan @ Limit(expressionExtractor(limitExpr),\n                          innerPlan @ VersionSpecificSortExtractor(order @ expressionExtractor(\n                                                                     sortExpr),\n                                                                   true,\n                                                                   Relation(relation))) =>\n          Some(\n            newStatement(plan)\n              .withLogicalPlanComment(innerPlan)\n              .selectAll()\n              .from(relation)\n              .orderby(sortExpr)\n              .limit(limitExpr)\n              .output(plan.output)\n              .asLogicalPlan(),\n            order\n          )\n\n        case _ => None\n      }\n    }\n  }\n\n  case class RelationOrSort(expressionExtractor: ExpressionExtractor) {\n    def unapply(source: LogicalPlan): Option[Relation] = {\n      val statementWithOrder = StatementWithOrder(expressionExtractor)\n      source match {\n        case Relation(relation)                        => Some(relation)\n        case statementWithOrder(Relation(relation), _) => Some(relation)\n        case _                                         => None\n      }\n    }\n  }\n\n  def fromLogicalPlan(\n      expressionExtractor: ExpressionExtractor): PartialFunction[LogicalPlan, Statement] = {\n    val sortPredicates = SortPredicates(expressionExtractor)\n    val relationOrSort = RelationOrSort(expressionExtractor)\n    return {\n      case plan @ Project(expressionExtractor(expr), Relation(relation)) =>\n        newStatement(plan)\n          .select(expr)\n          .from(relation)\n          .output(plan.output)\n\n      case plan @ Limit(expressionExtractor(expr), Relation(relation)) =>\n        newStatement(plan)\n          .selectAll()\n          .from(relation)\n          .limit(expr)\n          .output(plan.output)\n\n      case plan @ Filter(sortPredicates(filter), relationOrSort(relation)) => {\n        newStatement(plan)\n          .selectAll()\n          .from(relation)\n          .where(filter)\n          .output(plan.output)\n      }\n\n      case plan @ VersionSpecificAggregateExtractor(expressionExtractor(groupingExpr),\n                                                    expressionExtractor(aggregateExpr),\n                                                    relationOrSort(relation)) =>\n        newStatement(plan)\n          .select(aggregateExpr)\n          .from(relation)\n          .groupby(groupingExpr)\n          .output(plan.output)\n\n      case plan @ VersionSpecificWindowExtractor(expressionExtractor(windowExpressions),\n                                                 _,\n                                                 _,\n                                                 relationOrSort(relation)) => {\n        newStatement(plan)\n          .select(windowExpressions.map(exp => Raw(\"*,\") + exp))\n          .from(relation)\n          .output(plan.output)\n      }\n      // the last parameter is a spark hint for join\n      // SingleStore does its own optimizations under the hood, so we can safely ignore this parameter\n      case plan @ Join(relationOrSort(left),\n                       relationOrSort(right),\n                       joinType @ (Inner | Cross),\n                       sortPredicates(condition),\n                       _)\n          if getDMLConnProperties(left.reader.options, isOnExecutor = false) == getDMLConnProperties(\n            right.reader.options,\n            isOnExecutor = false) =>\n        newStatement(plan)\n          .selectAll()\n          .from(left)\n          .join(right, joinType)\n          .on(condition)\n          .output(plan.output)\n\n      // condition is required for {Left, Right, Full} outer joins\n      // the last parameter is a spark hint for join\n      // SingleStore does its own optimizations under the hood, so we can safely ignore this parameter\n      case plan @ Join(relationOrSort(left),\n                       relationOrSort(right),\n                       joinType @ (LeftOuter | RightOuter | FullOuter),\n                       Some(sortPredicates(condition)),\n                       _)\n          if getDMLConnProperties(left.reader.options, isOnExecutor = false) == getDMLConnProperties(\n            right.reader.options,\n            isOnExecutor = false) =>\n        newStatement(plan)\n          .selectAll()\n          .from(left)\n          .join(right, joinType)\n          .on(condition)\n          .output(plan.output)\n\n      // condition is not allowed for natural joins\n      // the last parameter is a spark hint for join\n      // SingleStore does its own optimizations under the hood, so we can safely ignore this parameter\n      case plan @ Join(relationOrSort(left), relationOrSort(right), NaturalJoin(joinType), None, _)\n          if getDMLConnProperties(left.reader.options, isOnExecutor = false) == getDMLConnProperties(\n            right.reader.options,\n            isOnExecutor = false) =>\n        newStatement(plan)\n          .selectAll()\n          .from(left)\n          .join(right, joinType)\n          .output(plan.output)\n    }\n  }\n\n  def fromTopLevelSort(\n      expressionExtractor: ExpressionExtractor): PartialFunction[LogicalPlan, LogicalPlan] = {\n    val statementWithOrder = StatementWithOrder(expressionExtractor)\n    val limitWithOrder     = LimitWithOrder(expressionExtractor)\n    return {\n      // for Disabled and AutomaticLite option do pushdown of the top-level sort expression\n      // parallel read won't be done in this case\n      case statementWithOrder(plan @ Relation(relation), _)\n          if relation.reader.options.enableParallelRead == Disabled ||\n            relation.reader.options.enableParallelRead == AutomaticLite => {\n        relation.reader.resultMustBeSorted = true\n        plan\n      }\n\n      // for Automatic and Forced option pushdown sort with limit but add top-level sort\n      // which will be done on a spark side\n      case limitWithOrder(plan @ Relation(relation), order)\n          if relation.reader.options.enableParallelRead == Automatic ||\n            relation.reader.options.enableParallelRead == Forced =>\n        Sort(order, global = true, plan)\n    }\n  }\n\n  // SQLGenContext is used to generate aliases during the codegen\n  // normalizedExprIdMap is a map from ExprId to its normalized index\n  // It is needed to make generated SQL queries deterministic\n  case class SQLGenContext(normalizedExprIdMap: HashMap[ExprId, Int],\n                           singlestoreVersion: SinglestoreVersion) {\n    val aliasGen: Iterator[String] = Iterator.from(1).map(i => s\"a$i\")\n    def nextAlias(): String        = aliasGen.next()\n\n    def singlestoreVersionAtLeast(version: String): Boolean =\n      singlestoreVersion.atLeast(version)\n\n    def ident(name: String, exprId: ExprId): String =\n      if (normalizedExprIdMap.contains(exprId)) {\n        Ident(s\"${name.substring(0, Math.min(name.length, 10))}#${normalizedExprIdMap(exprId)}\").sql\n      } else {\n        Ident(s\"${name.substring(0, Math.min(name.length, 10))}#${exprId.id}\").sql\n      }\n  }\n\n  object SQLGenContext {\n\n    var singlestoreVersion: Option[String] = None\n    private def getSinglestoreVersion(options: SinglestoreOptions): SinglestoreVersion =\n      singlestoreVersion match {\n        case Some(version) => SinglestoreVersion(version)\n        case None =>\n          singlestoreVersion = Some(JdbcHelpers.getSinglestoreVersion(options))\n          SinglestoreVersion(singlestoreVersion.get)\n      }\n\n    def apply(root: LogicalPlan, options: SinglestoreOptions): SQLGenContext = {\n      var normalizedExprIdMap = scala.collection.immutable.HashMap[ExprId, Int]()\n      val nextId              = Iterator.from(1)\n      root.foreach(plan =>\n        plan.output.foreach(f => {\n          if (!normalizedExprIdMap.contains(f.exprId)) {\n            normalizedExprIdMap = normalizedExprIdMap + (f.exprId -> nextId.next())\n          }\n        }))\n\n      new SQLGenContext(normalizedExprIdMap, getSinglestoreVersion(options))\n    }\n\n    def apply(options: SinglestoreOptions): SQLGenContext =\n      new SQLGenContext(HashMap.empty, getSinglestoreVersion(options))\n  }\n\n  case class SinglestoreVersion(major: Int, minor: Int, patch: Int) {\n\n    implicit val ordering: Ordering[SinglestoreVersion] =\n      Ordering.by(v => (v.major, v.minor, v.patch))\n\n    import Ordering.Implicits.infixOrderingOps\n\n    def atLeast(version: SinglestoreVersion): Boolean = {\n      this >= version\n    }\n\n    def atLeast(version: String): Boolean = {\n      atLeast(SinglestoreVersion(version))\n    }\n\n    override def toString: String = s\"${this.major}.${this.minor}.${this.patch}\"\n  }\n  object SinglestoreVersion {\n\n    def apply(version: String): SinglestoreVersion = {\n      val versionParts = version.split(\"\\\\.\")\n      if (versionParts.size != 3)\n        throw new IllegalArgumentException(\n          \"Singlestore version should contain three parts (major, minor, patch)\")\n      new SinglestoreVersion(Integer.parseInt(versionParts(0)),\n                             Integer.parseInt(versionParts(1)),\n                             Integer.parseInt(versionParts(2)))\n    }\n  }\n\n  case class ExpressionExtractor(context: SQLGenContext) {\n\n    protected lazy val log: Logger = LoggerFactory.getLogger(getClass.getName)\n\n    def unapply(arg: Expression): Option[Joinable] = {\n      val out = ExpressionGen.apply(this).lift(arg)\n\n      if (out.isEmpty && log.isTraceEnabled) {\n        val argStr: String = try {\n          arg.asCode\n        } catch {\n          case e: NullPointerException =>\n            s\"${arg.prettyName} (failed to convert expression to string)\"\n        }\n        log.trace(s\"Warning: SingleStore SQL pushdown was unable to convert expression: $argStr\")\n      }\n\n      out\n    }\n\n    // None -> Some(None) nothing to compile results in no SQL\n    // Some(good expr) -> Some(Some(sql)) we can compile, results in SQL\n    // Some(bad expr) -> None failed to compile, unapply does not match\n    def unapply(arg: Option[Expression]): Option[Option[Joinable]] = arg match {\n      case None             => Some(None)\n      case Some(expression) => ExpressionGen.apply(this).lift(expression).map(j => Some(j))\n    }\n\n    // Seq() -> Some(None) nothing to compile results in no SQL\n    // Seq(good expressions) -> Some(Some(sql)) we can compile, results in SQL\n    // Seq(at least one bad expression) -> None failed to compile, unapply does not match\n    def unapply(args: Seq[Expression]): Option[Option[Joinable]] = {\n      if (args.isEmpty) {\n        Some(None)\n      } else {\n        // TODO: PLAT-4670 check how this can be improved to enable pushdown for queries like \"SELECT CONCAT(first_name, first_name) FROM users\"\n        if (args.lengthCompare(1) > 0) {\n          val expressionNames = new mutable.HashSet[String]()\n          val hasDuplicates = args.exists({\n            case a @ NamedExpression(name, _) => !expressionNames.add(s\"${name}#${a.exprId.id}\")\n            case _                            => false\n          })\n          if (hasDuplicates) return None\n        }\n\n        args\n          .map(ExpressionGen.apply(this).lift)\n          .reduce[Option[Joinable]] {\n            case (Some(left), Some(right)) => Some(left + \",\" + right)\n            case _                         => None\n          }\n          .map(j => Some(j))\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SQLHelper.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.JdbcHelpers.{executeQuery, getDDLConnProperties}\nimport org.apache.spark.sql.{Row, SparkSession}\nimport org.apache.spark.sql.catalyst.util.CaseInsensitiveMap\nimport org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils\n\nobject SQLHelper extends LazyLogging {\n  implicit class QueryMethods(spark: SparkSession) {\n    private def singlestoreQuery(db: Option[String],\n                                 query: String,\n                                 variables: Any*): Iterator[Row] = {\n      val ctx = spark.sqlContext\n      var opts = ctx.getAllConfs.collect {\n        case (k, v) if k.startsWith(DefaultSource.SINGLESTORE_GLOBAL_OPTION_PREFIX) =>\n          k.stripPrefix(DefaultSource.SINGLESTORE_GLOBAL_OPTION_PREFIX) -> v\n        case (k, v) if k.startsWith(DefaultSource.MEMSQL_GLOBAL_OPTION_PREFIX) =>\n          k.stripPrefix(DefaultSource.MEMSQL_GLOBAL_OPTION_PREFIX) -> v\n      }\n\n      if (db.isDefined) {\n        val dbValue = db.get\n        if (dbValue.isEmpty) {\n          opts -= \"database\"\n        } else {\n          opts += (\"database\" -> dbValue)\n        }\n      }\n\n      val conf = SinglestoreOptions(CaseInsensitiveMap(opts), spark.sparkContext)\n      val conn =\n        SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n      try {\n        executeQuery(conn, query, variables: _*)\n      } finally {\n        conn.close()\n      }\n    }\n\n    def executeSinglestoreQueryDB(db: String, query: String, variables: Any*): Iterator[Row] = {\n      singlestoreQuery(Some(db), query, variables: _*)\n    }\n\n    def executeSinglestoreQuery(query: String, variables: Any*): Iterator[Row] = {\n      singlestoreQuery(None, query, variables: _*)\n    }\n\n    @Deprecated def executeMemsqlQueryDB(db: String,\n                                         query: String,\n                                         variables: Any*): Iterator[Row] = {\n      singlestoreQuery(Some(db), query, variables: _*)\n    }\n\n    @Deprecated def executeMemsqlQuery(query: String, variables: Any*): Iterator[Row] = {\n      singlestoreQuery(None, query, variables: _*)\n    }\n  }\n\n  def executeSinglestoreQueryDB(spark: SparkSession,\n                                db: String,\n                                query: String,\n                                variables: Any*): Iterator[Row] = {\n    spark.executeSinglestoreQueryDB(db, query, variables: _*)\n  }\n\n  def executeSinglestoreQuery(spark: SparkSession,\n                              query: String,\n                              variables: Any*): Iterator[Row] = {\n    spark.executeSinglestoreQuery(query, variables: _*)\n  }\n\n  @Deprecated def executeMemsqlQueryDB(spark: SparkSession,\n                                       db: String,\n                                       query: String,\n                                       variables: Any*): Iterator[Row] = {\n    spark.executeSinglestoreQueryDB(db, query, variables: _*)\n  }\n\n  @Deprecated def executeMemsqlQuery(spark: SparkSession,\n                                     query: String,\n                                     variables: Any*): Iterator[Row] = {\n    spark.executeSinglestoreQuery(query, variables: _*)\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SQLPushdownRule.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext}\nimport org.apache.spark.sql.SparkSession\nimport org.apache.spark.sql.catalyst.plans.logical._\nimport org.apache.spark.sql.catalyst.rules.Rule\n\nclass SQLPushdownRule extends Rule[LogicalPlan] {\n  override def apply(root: LogicalPlan): LogicalPlan = {\n    var context: SQLGenContext = null\n    val needsPushdown = root\n      .find({\n        case SQLGen.Relation(r: SQLGen.Relation) if !r.reader.isFinal =>\n          context = SQLGenContext(root, r.reader.options)\n          true\n        case _ => false\n      })\n      .isDefined\n\n    if (!needsPushdown) {\n      return root\n    }\n\n    if (log.isTraceEnabled) {\n      log.trace(s\"Optimizing plan:\\n${root.treeString(true)}\")\n    }\n\n    // We first need to set a SQLGenContext in every reader.\n    // This transform is done to ensure that we will generate the same aliases in the same queries.\n    val normalized = root.transform({\n      case SQLGen.Relation(relation) =>\n        relation.toLogicalPlan(\n          relation.output,\n          relation.reader.query,\n          relation.reader.variables,\n          relation.reader.isFinal,\n          context\n        )\n    })\n\n    // Second, we need to rename the outputs of each SingleStore relation in the tree.  This transform is\n    // done to ensure that we can handle projections which involve ambiguous column name references.\n    var ptr, nextPtr = normalized.transform({\n      case SQLGen.Relation(relation) => relation.renameOutput\n    })\n\n    val expressionExtractor = ExpressionExtractor(context)\n    val transforms =\n      List(\n        // do all rewrites except top-level sort, e.g. Project([a,b,c], Relation(select * from foo))\n        SQLGen.fromLogicalPlan(expressionExtractor).andThen(_.asLogicalPlan()),\n        // do rewrites with top-level Sort, e.g. Sort(a, Limit(10, Relation(select * from foo))\n        // won't be done for relations with parallel read enabled\n        SQLGen.fromTopLevelSort(expressionExtractor),\n      )\n\n    // Run our transforms in a loop until the tree converges\n    do {\n      ptr = nextPtr\n      nextPtr = transforms.foldLeft(ptr)(_.transformUp(_))\n    } while (!ptr.fastEquals(nextPtr))\n\n    // Finalize all the relations in the tree and perform casts into the expected output datatype for Spark\n    val out = ptr.transform({\n      case SQLGen.Relation(relation) if !relation.isFinal => relation.castOutputAndFinalize\n    })\n\n    if (log.isTraceEnabled) {\n      log.trace(s\"Optimized Plan:\\n${out.treeString(true)}\")\n    }\n\n    out\n  }\n}\n\nobject SQLPushdownRule {\n  def injected(session: SparkSession): Boolean = {\n    session.experimental.extraOptimizations\n      .exists(s => s.isInstanceOf[SQLPushdownRule])\n  }\n\n  def ensureInjected(session: SparkSession): Unit = {\n    if (!injected(session)) {\n      session.experimental.extraOptimizations ++= Seq(new SQLPushdownRule)\n    }\n  }\n\n  def ensureRemoved(session: SparkSession): Unit = {\n    session.experimental.extraOptimizations = session.experimental.extraOptimizations\n      .filterNot(s => s.isInstanceOf[SQLPushdownRule])\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreBatchInsertWriter.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.Connection\nimport java.util.Base64\n\nimport com.singlestore.spark.JdbcHelpers.{getDDLConnProperties, getDMLConnProperties}\nimport org.apache.spark.sql.catalyst.TableIdentifier\nimport org.apache.spark.sql.types.{BinaryType, StructType}\nimport org.apache.spark.sql.{Row, SaveMode}\n\nimport scala.collection.mutable.ListBuffer\nimport scala.concurrent.ExecutionContext\n\n// TODO: extend it from DataWriterFactory\nclass BatchInsertWriterFactory(table: TableIdentifier, conf: SinglestoreOptions)\n    extends WriterFactory\n    with LazyLogging {\n\n  def createDataWriter(schema: StructType,\n                       partitionId: Int,\n                       attemptNumber: Int,\n                       isReferenceTable: Boolean,\n                       mode: SaveMode): DataWriter[Row] = {\n    val columnNames = schema.map(s => SinglestoreDialect.quoteIdentifier(s.name))\n    val queryPrefix = s\"INSERT INTO ${table.quotedString} (${columnNames.mkString(\", \")}) VALUES \"\n    val querySuffix = s\" ON DUPLICATE KEY UPDATE ${conf.onDuplicateKeySQL.get}\"\n\n    val rowTemplate = \"(\" + schema\n      .map(x =>\n        x.dataType match {\n          case BinaryType => \"FROM_BASE64(?)\"\n          case _          => \"?\"\n      })\n      .mkString(\",\") + \")\"\n    def valueTemplate(rows: Int): String =\n      List.fill(rows)(rowTemplate).mkString(\",\")\n    val fullBatchQuery = queryPrefix + valueTemplate(conf.insertBatchSize) + querySuffix\n\n    val conn = SinglestoreConnectionPool.getConnection(if (isReferenceTable) {\n      getDDLConnProperties(conf, isOnExecutor = true)\n    } else {\n      getDMLConnProperties(conf, isOnExecutor = true)\n    })\n    conn.setAutoCommit(false)\n\n    def writeBatch(buff: ListBuffer[Row]): Long = {\n      if (buff.isEmpty) {\n        0\n      } else {\n        val rowsCount = buff.size\n        val query = if (rowsCount == conf.insertBatchSize) {\n          fullBatchQuery\n        } else {\n          queryPrefix + valueTemplate(rowsCount) + querySuffix\n        }\n\n        val stmt = conn.prepareStatement(query)\n        try {\n          for {\n            (row, i) <- buff.iterator.zipWithIndex\n            rowLength = row.size\n            j <- 0 until rowLength\n          } row(j) match {\n            case bytes: Array[Byte] =>\n              stmt.setObject(i * rowLength + j + 1, Base64.getEncoder.encode(bytes))\n            case obj =>\n              stmt.setObject(i * rowLength + j + 1, obj)\n          }\n          stmt.executeUpdate()\n        } finally {\n          stmt.close()\n          conn.commit()\n        }\n      }\n    }\n\n    new BatchInsertWriter(conf.insertBatchSize, writeBatch, conn)\n  }\n}\n\nclass BatchInsertWriter(batchSize: Int, writeBatch: ListBuffer[Row] => Long, conn: Connection)\n    extends DataWriter[Row] {\n  var buff: ListBuffer[Row] = ListBuffer.empty[Row]\n\n  override def write(row: Row): Unit = {\n    buff += row\n    if (buff.size >= batchSize) {\n      writeBatch(buff)\n      buff = ListBuffer.empty[Row]\n    }\n  }\n\n  override def commit(): WriterCommitMessage = {\n    try {\n      writeBatch(buff)\n      buff = ListBuffer.empty[Row]\n    } finally {\n      conn.close()\n    }\n    new WriteSuccess\n  }\n\n  override def abort(e: Exception): Unit = {\n    buff = ListBuffer.empty[Row]\n    if (!conn.isClosed) {\n      conn.abort(ExecutionContext.global)\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreConnectionPool.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.Connection\nimport java.util.Properties\nimport org.apache.commons.dbcp2.{BasicDataSource, BasicDataSourceFactory}\nimport scala.collection.mutable\n\nobject SinglestoreConnectionPool {\n  private var dataSources = new mutable.HashMap[Properties, BasicDataSource]()\n\n  private def deleteEmptyDataSources(): Unit = {\n    dataSources = dataSources.filter(pair => {\n      val dataSource = pair._2\n      if (dataSource.getNumActive + dataSource.getNumIdle == 0) {\n        dataSource.close()\n        false\n      } else {\n        true\n      }\n    })\n  }\n\n  def getConnection(properties: Properties): Connection = {\n    this.synchronized({\n      dataSources\n        .getOrElse(\n          properties, {\n            deleteEmptyDataSources()\n            val newDataSource = BasicDataSourceFactory.createDataSource(properties)\n            newDataSource.addConnectionProperty(\"connectionAttributes\", \n                                                properties.getProperty(\"connectionAttributes\"))\n            dataSources += (properties -> newDataSource)\n            newDataSource\n          }\n        )\n        .getConnection\n    })\n  }\n\n  def close(): Unit = {\n    this.synchronized({\n      dataSources.foreach(pair => pair._2.close())\n      dataSources = new mutable.HashMap[Properties, BasicDataSource]()\n    })\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreConnectionPoolOptions.scala",
    "content": "package com.singlestore.spark\n\ncase class SinglestoreConnectionPoolOptions(enabled: Boolean,\n                                            MaxOpenConns: Int,\n                                            MaxIdleConns: Int,\n                                            MinEvictableIdleTimeMs: Long,\n                                            TimeBetweenEvictionRunsMS: Long,\n                                            MaxWaitMS: Long,\n                                            MaxConnLifetimeMS: Long)\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreDialect.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.Types\n\nimport org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils\nimport org.apache.spark.sql.jdbc.{JdbcDialect, JdbcType}\nimport org.apache.spark.sql.types._\n\ncase object SinglestoreDialect extends JdbcDialect {\n  override def canHandle(url: String): Boolean = url.startsWith(\"jdbc:memsql\")\n\n  val SINGLESTORE_DECIMAL_MAX_SCALE = 30\n\n  override def getJDBCType(dt: DataType): Option[JdbcType] = dt match {\n    case BooleanType   => Option(JdbcType(\"BOOL\", Types.BOOLEAN))\n    case ByteType      => Option(JdbcType(\"TINYINT\", Types.TINYINT))\n    case ShortType     => Option(JdbcType(\"SMALLINT\", Types.SMALLINT))\n    case FloatType     => Option(JdbcType(\"FLOAT\", Types.FLOAT))\n    case TimestampType => Option(JdbcType(\"TIMESTAMP(6)\", Types.TIMESTAMP))\n    case dt: DecimalType if (dt.scale <= SINGLESTORE_DECIMAL_MAX_SCALE) =>\n      Option(JdbcType(s\"DECIMAL(${dt.precision}, ${dt.scale})\", Types.DECIMAL))\n    case dt: DecimalType =>\n      throw new IllegalArgumentException(\n        s\"Too big scale specified(${dt.scale}). SingleStore DECIMAL maximum scale is ${SINGLESTORE_DECIMAL_MAX_SCALE}\")\n    case NullType =>\n      throw new IllegalArgumentException(\n        \"No corresponding SingleStore type found for NullType. If you want to use NullType, please write to an already existing SingleStore table.\")\n    case t => JdbcUtils.getCommonJDBCType(t)\n  }\n\n  override def getCatalystType(sqlType: Int,\n                               typeName: String,\n                               size: Int,\n                               md: MetadataBuilder): Option[DataType] = {\n    (sqlType, typeName) match {\n      case (Types.REAL, \"FLOAT\") => Option(FloatType)\n      case (Types.BIT, \"BIT\")    => Option(BinaryType)\n      // JDBC driver returns incorrect SQL type for BIT\n      // TODO delete after PLAT-6829 is fixed\n      case (Types.BOOLEAN, \"BIT\")               => Option(BinaryType)\n      case (Types.TINYINT, \"TINYINT\")           => Option(ByteType)\n      case (Types.SMALLINT, \"SMALLINT\")         => Option(ShortType)\n      case (Types.INTEGER, \"SMALLINT\")          => Option(IntegerType)\n      case (Types.INTEGER, \"SMALLINT UNSIGNED\") => Option(IntegerType)\n      case (Types.DECIMAL, \"DECIMAL\") => {\n        if (size > DecimalType.MAX_PRECISION) {\n          throw new IllegalArgumentException(\n            s\"DECIMAL precision ${size} exceeds max precision ${DecimalType.MAX_PRECISION}\")\n        } else {\n          Option(\n            DecimalType(size, md.build().getLong(\"scale\").toInt)\n          )\n        }\n      }\n      case _ => None\n    }\n  }\n\n  override def quoteIdentifier(colName: String): String = {\n    s\"`${colName.replace(\"`\", \"``\")}`\"\n  }\n\n  override def isCascadingTruncateTable(): Option[Boolean] = Some(false)\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreLoadDataWriter.scala",
    "content": "package com.singlestore.spark\n\nimport java.io.{IOException, InputStream, OutputStream, PipedInputStream, PipedOutputStream}\nimport java.nio.ByteBuffer\nimport java.nio.charset.StandardCharsets\nimport java.sql.Connection\nimport java.util.Base64\nimport java.util.zip.GZIPOutputStream\nimport com.singlestore.spark.JdbcHelpers.{getDDLConnProperties, getDMLConnProperties}\nimport com.singlestore.spark.SinglestoreOptions.CompressionType\nimport com.singlestore.spark.vendor.apache.SchemaConverters\nimport net.jpountz.lz4.LZ4FrameOutputStream\nimport org.apache.avro.Schema\nimport org.apache.avro.generic.{GenericData, GenericDatumWriter, GenericRecord}\nimport org.apache.avro.io.EncoderFactory\nimport org.apache.commons.dbcp2.DelegatingStatement\nimport org.apache.spark.sql.catalyst.TableIdentifier\nimport org.apache.spark.sql.types.{BinaryType, StructType}\nimport org.apache.spark.sql.{Row, SaveMode}\n\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.duration.Duration\nimport scala.concurrent.{Await, ExecutionContext, Future}\nimport scala.util.{Failure, Try}\n\nabstract class WriterCommitMessage extends Serializable {}\ncase class WriteSuccess()          extends WriterCommitMessage\n\nabstract class DataWriter[T] {\n  def write(record: T): Unit\n  def commit(): WriterCommitMessage\n  def abort(e: Exception): Unit\n}\n\nabstract class WriterFactory extends Serializable {\n  def createDataWriter(schema: StructType,\n                       partitionId: Int,\n                       attemptNumber: Int,\n                       isReferenceTable: Boolean,\n                       mode: SaveMode): DataWriter[Row]\n}\n\n// TODO: extend it from DataWriterFactory\nclass LoadDataWriterFactory(table: TableIdentifier, conf: SinglestoreOptions)\n    extends WriterFactory\n    with LazyLogging {\n\n  final val BUFFER_SIZE = 524288\n\n  type ImplementsSetInfileStream = {\n    def setNextLocalInfileInputStream(input: InputStream)\n  }\n\n  private def createLoadDataQuery(schema: StructType, mode: SaveMode, avroSchema: Schema): String = {\n    val ext = conf.loadDataCompression match {\n      case CompressionType.GZip => \"gz\"\n      case CompressionType.LZ4 => \"lz4\"\n      case CompressionType.Skip => \"tsv\"\n    }\n\n    def tempColName(colName: String) = s\"@${colName}_tmp\"\n\n    val columnNames = schema.map(s =>\n      if (s.dataType == BinaryType) {\n        tempColName(s.name)\n      } else {\n        SinglestoreDialect.quoteIdentifier(s.name)\n    })\n\n    val loadDataFormat = conf.loadDataFormat\n    val querySetPart =\n      if (loadDataFormat == SinglestoreOptions.LoadDataFormat.Avro) \"\"\n      else {\n        val binaryColumns = schema.filter(_.dataType == BinaryType)\n        if (binaryColumns.isEmpty) {\n          \"\"\n        } else {\n          val operations = binaryColumns\n            .map(s =>\n              s\"${SinglestoreDialect.quoteIdentifier(s.name)} = FROM_BASE64(${tempColName(s.name)})\")\n          s\"SET ${operations.mkString(\", \")}\"\n        }\n      }\n\n    val queryErrorHandlingPart = mode match {\n      // If SaveMode is Ignore - skip all duplicate key errors\n      case SaveMode.Ignore => \"SKIP DUPLICATE KEY ERRORS\"\n      case _ =>\n        conf.overwriteBehavior match {\n          // If SaveMode is NOT Ignore and OverwriteBehavior is Merge - replace all duplicates\n          case Merge => \"REPLACE\"\n          case _     => \"\"\n        }\n    }\n    val maxErrorsPart      = s\"MAX_ERRORS ${conf.maxErrors}\"\n    val queryPrefix        = s\"LOAD DATA LOCAL INFILE '###.$ext'\"\n    val queryEnding = if (loadDataFormat == SinglestoreOptions.LoadDataFormat.Avro) {\n      val nullableSchemas = for ((field, index) <- schema.fields.zipWithIndex)\n        yield\n          AvroSchemaHelper.resolveNullableType(avroSchema.getFields.get(index).schema(),\n                                               field.nullable)\n      val avroSchemaParts = for ((field, index) <- schema.fields.zipWithIndex) yield {\n        val avroSchemaMapping =\n          s\"${SinglestoreDialect.quoteIdentifier(field.name)} <- %::${SinglestoreDialect\n            .quoteIdentifier(field.name)}\"\n        if (field.nullable) {\n          s\"$avroSchemaMapping::${nullableSchemas(index).getType.getName}\"\n        } else {\n          avroSchemaMapping\n        }\n      }\n      val avroMapping = avroSchemaParts.mkString(\"( \", \", \", \" )\")\n      s\"INTO TABLE ${table.quotedString} FORMAT AVRO $avroMapping SCHEMA '${avroSchema.toString}'\"\n    } else {\n      s\"INTO TABLE ${table.quotedString} (${columnNames.mkString(\", \")})\"\n    }\n    val query =\n      List[String](queryPrefix, queryErrorHandlingPart, queryEnding, querySetPart, maxErrorsPart)\n        .filter(s => !s.isEmpty)\n        .mkString(\" \")\n\n    query\n  }\n\n  private def createStreams(): (InputStream, OutputStream) = {\n    val basestream  = new PipedOutputStream\n    val inputstream = new PipedInputStream(basestream, BUFFER_SIZE)\n\n    val (ext, outputstream) = conf.loadDataCompression match {\n      case CompressionType.GZip =>\n        // With gzip default 1 we get a 50% improvement in bandwidth\n        // (up to 16 Mps) over gzip default 6 on customer workload.\n        //\n        (\"gz\", new GZIPOutputStream(basestream) { { `def`.setLevel(1) } })\n\n      case CompressionType.LZ4 =>\n        (\"lz4\", new LZ4FrameOutputStream(basestream))\n\n      case CompressionType.Skip =>\n        (\"tsv\", basestream)\n    }\n\n    (inputstream, outputstream)\n  }\n\n  private def startStatementExecution(conn: Connection, query: String, inputstream: InputStream): Future[Long] = {\n    Future[Long] {\n      try {\n        val stmt = conn.createStatement()\n        try {\n          stmt\n            .asInstanceOf[DelegatingStatement]\n            .getInnermostDelegate\n            .asInstanceOf[ImplementsSetInfileStream]\n            .setNextLocalInfileInputStream(inputstream)\n\n          log.debug(s\"Executing SQL:\\n$query\")\n          stmt.executeUpdate(query)\n        } finally {\n          stmt.close()\n        }\n      } finally {\n        inputstream.close()\n      }\n    }\n  }\n\n  def createDataWriter(schema: StructType,\n                       partitionId: Int,\n                       attemptNumber: Int,\n                       isReferenceTable: Boolean,\n                       mode: SaveMode): DataWriter[Row] = {\n    val avroSchema: Schema = if (conf.loadDataFormat == SinglestoreOptions.LoadDataFormat.Avro) {\n      SchemaConverters.toAvroType(schema)\n    } else {\n      null\n    }\n    val query = createLoadDataQuery(schema, mode, avroSchema)\n\n    val conn = SinglestoreConnectionPool.getConnection(if (isReferenceTable) {\n      getDDLConnProperties(conf, isOnExecutor = true)\n    } else {\n      getDMLConnProperties(conf, isOnExecutor = true)\n    })\n    conn.setAutoCommit(false);\n\n    val createDatabaseWriter: () => (OutputStream, Future[Long]) = () => {\n      val (inputstream, outputstream) = createStreams()\n      val writer = startStatementExecution(conn, query, inputstream)\n      (outputstream, writer)\n    }\n\n    if (conf.loadDataFormat == SinglestoreOptions.LoadDataFormat.Avro) {\n      new AvroDataWriter(avroSchema, createDatabaseWriter, conn, conf.insertBatchSize)\n    } else {\n      new LoadDataWriter(createDatabaseWriter, conn, conf.insertBatchSize)\n    }\n  }\n}\n\nclass LoadDataWriter(createDatabaseWriter: () => (OutputStream, Future[Long]), conn: Connection, batchSize: Int)\n    extends DataWriter[Row] {\n\n  private var (outputstream, writeFuture) = createDatabaseWriter()\n  private var rowsInBatch = 0\n\n  override def write(row: Row): Unit = {\n    if (rowsInBatch >= batchSize) {\n      Try(outputstream.close())\n      Await.result(writeFuture, Duration.Inf)\n      val (newOutputStream, newWriteFuture) = createDatabaseWriter()\n      outputstream = newOutputStream\n      writeFuture = newWriteFuture\n      rowsInBatch = 0\n    }\n\n    val rowLength = row.size\n    for (i <- 0 until rowLength) {\n      // We tried using off the shelf CSVWriter, but found it qualitatively slower.\n      // The csv writer below has been benchmarked at 90 Mps going to a null output stream\n      val value = row(i) match {\n        case null => \"\\\\N\".getBytes(StandardCharsets.UTF_8)\n        // NOTE: We special case booleans because SingleStore/MySQL's LOAD DATA\n        // semantics only accept \"1\" as true in boolean/tinyint(1) columns\n        case true               => \"1\".getBytes(StandardCharsets.UTF_8)\n        case false              => \"0\".getBytes(StandardCharsets.UTF_8)\n        case bytes: Array[Byte] => Base64.getEncoder.encode(bytes)\n        case rawValue => {\n          var valueString = rawValue.toString\n\n          if (valueString.indexOf('\\\\') != -1) {\n            valueString = valueString.replace(\"\\\\\", \"\\\\\\\\\")\n          }\n          if (valueString.indexOf('\\n') != -1) {\n            valueString = valueString.replace(\"\\n\", \"\\\\n\")\n          }\n          if (valueString.indexOf('\\t') != -1) {\n            valueString = valueString.replace(\"\\t\", \"\\\\t\")\n          }\n\n          valueString.getBytes(StandardCharsets.UTF_8)\n        }\n      }\n      outputstream.write(value)\n      outputstream.write(if (i < rowLength - 1) '\\t' else '\\n')\n    }\n\n    rowsInBatch += 1\n  }\n\n  override def commit(): WriterCommitMessage = {\n    try {\n      Try(outputstream.close())\n      Await.result(writeFuture, Duration.Inf)\n      conn.commit()\n    } finally {\n      conn.close()\n    }\n\n    new WriteSuccess\n  }\n\n  override def abort(writerException: Exception): Unit = {\n    if (!conn.isClosed) {\n      conn.abort(ExecutionContext.global)\n    }\n    Try(outputstream.close())\n    try {\n      Await.result(writeFuture, Duration.Inf)\n    } catch {\n      case readerException: Exception => {\n        // if we got pipe closed error from the thread that writes data to stream\n        // then the error actual occurred in the thread that ran the query\n        // and we need to return the actual error\n        if (writerException\n              .isInstanceOf[IOException] && writerException.getMessage.contains(\"Pipe closed\")) {\n          throw readerException\n        }\n      }\n    }\n  }\n}\n\nclass AvroDataWriter(avroSchema: Schema,\n                     createDatabaseWriter: () => (OutputStream, Future[Long]),\n                     conn: Connection,\n                     batchSize: Int)\n    extends DataWriter[Row] {\n\n  private var (outputstream, writeFuture) = createDatabaseWriter()\n  private var rowsInBatch = 0\n  private var encoder     = EncoderFactory.get().binaryEncoder(outputstream, null)\n  \n  val datumWriter = new GenericDatumWriter[GenericRecord](avroSchema)\n  val record      = new GenericData.Record(avroSchema)\n  \n  val conversionFunctions: Any => Any = {\n    case d: java.math.BigDecimal =>\n      d.toString\n    case s: Short =>\n      s.toInt\n    case bytes: Array[Byte] =>\n      ByteBuffer.wrap(bytes)\n    case b: Byte =>\n      b.toInt\n    case num => num\n  }\n\n  override def write(row: Row): Unit = {\n    if (rowsInBatch >= batchSize) {\n      encoder.flush()\n      Try(outputstream.close())\n      Await.result(writeFuture, Duration.Inf)\n      val (newOutputStream, newWriteFuture) = createDatabaseWriter()\n      outputstream = newOutputStream\n      writeFuture = newWriteFuture\n      encoder = EncoderFactory.get().binaryEncoder(outputstream, null)\n      rowsInBatch = 0\n    }\n\n    val rowLength = row.size\n    for (i <- 0 until rowLength) {\n      record.put(i, conversionFunctions(row(i)))\n    }\n    datumWriter.write(record, encoder)\n\n    rowsInBatch += 1\n  }\n\n  override def commit(): WriterCommitMessage = {\n    try {\n      encoder.flush()\n      Try(outputstream.close())\n      Await.result(writeFuture, Duration.Inf)\n      conn.commit()\n    } finally {\n      conn.close()\n    }\n\n    new WriteSuccess\n  }\n\n  override def abort(writerException: Exception): Unit = {\n    if (!conn.isClosed) {\n      conn.abort(ExecutionContext.global)\n    }\n    Try(outputstream.close())\n    try {\n      Await.result(writeFuture, Duration.Inf)\n    } catch {\n      case readerException: Exception => {\n        // if we got pipe closed error from the thread that writes data to stream\n        // then the error actual occurred in the thread that ran the query\n        // and we need to return the actual error\n        if (writerException\n              .isInstanceOf[IOException] && writerException.getMessage.contains(\"Pipe closed\")) {\n          throw readerException\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreOptions.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SinglestoreOptions.TableKey\nimport org.apache.spark.sql.catalyst.TableIdentifier\nimport org.apache.spark.sql.catalyst.parser.CatalystSqlParser\nimport org.apache.spark.sql.catalyst.util.CaseInsensitiveMap\nimport org.apache.spark.SparkContext\nimport org.apache.spark.sql.types.StructType\n\ncase class SinglestoreOptions(\n    ddlEndpoint: String,\n    dmlEndpoints: List[String],\n    user: String,\n    password: String,\n    database: Option[String],\n    jdbcExtraOptions: Map[String, String],\n    enableAsserts: Boolean,\n    // read options\n    disablePushdown: Boolean,\n    enableParallelRead: ParallelReadEnablement,\n    parallelReadFeatures: List[ParallelReadType],\n    parallelReadTableCreationTimeoutMS: Long,\n    parallelReadMaterializedTableCreationTimeoutMS: Long,\n    parallelReadMaxNumPartitions: Int,\n    parallelReadNumPartitions: Int,\n    parallelReadRepartition: Boolean,\n    parallelReadRepartitionColumns: Set[String],\n    // write options\n    overwriteBehavior: OverwriteBehavior,\n    loadDataCompression: SinglestoreOptions.CompressionType.Value,\n    loadDataFormat: SinglestoreOptions.LoadDataFormat.Value,\n    tableKeys: List[TableKey],\n    onDuplicateKeySQL: Option[String],\n    maxErrors: Int,\n    insertBatchSize: Int,\n    createRowstoreTable: Boolean,\n    driverConnectionPoolOptions: SinglestoreConnectionPoolOptions,\n    executorConnectionPoolOptions: SinglestoreConnectionPoolOptions,\n    sparkVersion: String,\n    customSchema: Option[StructType]\n) extends LazyLogging {\n\n  def assert(condition: Boolean, message: String) = {\n    if (!condition) {\n      if (enableAsserts) {\n        throw new AssertionError(message)\n      } else {\n        log.trace(s\"assertion failed: ${message}\")\n      }\n    }\n  }\n}\n\nobject SinglestoreOptions extends LazyLogging {\n  abstract class OptionEnum extends Enumeration {\n    def fromString(s: String): Option[Value] =\n      values.find(_.toString.toLowerCase() == s.toLowerCase())\n\n    lazy val valuesString = values.mkString(\", \")\n    def fromOption(optionName: String, source: Option[String], default: Value): Value =\n      source\n        .orElse(Some(default.toString))\n        .flatMap(fromString)\n        .getOrElse(\n          sys.error(\n            s\"Option '$optionName' must be one of the following values: ${valuesString}\"\n          )\n        )\n  }\n\n  object LoadDataFormat extends OptionEnum {\n    val CSV, Avro = Value\n  }\n\n  object CompressionType extends OptionEnum {\n    val GZip, LZ4, Skip = Value\n  }\n\n  object TableKeyType extends OptionEnum {\n    val Primary, Columnstore, Unique, Shard, Key = Value\n  }\n\n  case class TableKey(keyType: TableKeyType.Value,\n                      name: Option[String] = None,\n                      columns: String = \"\")\n\n  private val singlestoreOptionNames = collection.mutable.Set[String]()\n\n  private def newOption(name: String): String = {\n    singlestoreOptionNames += name.toLowerCase()\n    name\n  }\n\n  final val DDL_ENDPOINT    = newOption(\"ddlEndpoint\")\n  final val DML_ENDPOINTS   = newOption(\"dmlEndpoints\")\n  final val CLIENT_ENDPOINT = newOption(\"clientEndpoint\")\n\n  final val USER     = newOption(\"user\")\n  final val PASSWORD = newOption(\"password\")\n\n  final val DATABASE   = newOption(\"database\")\n  final val QUERY      = newOption(\"query\")\n  final val TABLE_NAME = newOption(\"dbtable\")\n  final val PATH       = newOption(\"path\")\n\n  // Write options\n  final val TRUNCATE               = newOption(\"truncate\")\n  final val OVERWRITE_BEHAVIOR     = newOption(\"overwriteBehavior\")\n  final val PARALLEL_READ_FEATURES = newOption(\"parallelRead.features\")\n  final val PARALLEL_READ_TABLE_CREATION_TIMEOUT_MS = newOption(\n    \"parallelRead.tableCreationTimeoutMS\")\n  final val PARALLEL_READ_MATERIALIZED_TABLE_CREATION_TIMEOUT_MS = newOption(\n    \"parallelRead.materializedTableCreationTimeoutMS\")\n  final val PARALLEL_READ_REPARTITION         = newOption(\"parallelRead.repartition\")\n  final val PARALLEL_READ_REPARTITION_COLUMNS = newOption(\"parallelRead.repartition.columns\")\n  final val PARALLEL_READ_MAX_NUM_PARTITIONS  = newOption(\"parallelRead.maxNumPartitions\")\n  final val PARALLEL_READ_NUM_PARTITIONS      = newOption(\"parallelRead.numPartitions\")\n\n  final val LOAD_DATA_COMPRESSION = newOption(\"loadDataCompression\")\n  final val TABLE_KEYS            = newOption(\"tableKey\")\n  final val LOAD_DATA_FORMAT      = newOption(\"loadDataFormat\")\n  final val ON_DUPLICATE_KEY_SQL  = newOption(\"onDuplicateKeySQL\")\n  final val INSERT_BATCH_SIZE     = newOption(\"insertBatchSize\")\n  final val MAX_ERRORS            = newOption(\"maxErrors\")\n  final val CREATE_ROWSTORE_TABLE = newOption(\"createRowstoreTable\")\n\n  final val ENABLE_ASSERTS       = newOption(\"enableAsserts\")\n  final val DISABLE_PUSHDOWN     = newOption(\"disablePushdown\")\n  final val ENABLE_PARALLEL_READ = newOption(\"enableParallelRead\")\n  final val CUSTOM_SCHEMA        = newOption(\"customSchema\")\n\n  final val DRIVER_CONNECTION_POOL_ENABLED        = newOption(\"driverConnectionPool.Enabled\")\n  final val DRIVER_CONNECTION_POOL_MAX_OPEN_CONNS = newOption(\"driverConnectionPool.MaxOpenConns\")\n  final val DRIVER_CONNECTION_POOL_MAX_IDLE_CONNS = newOption(\"driverConnectionPool.MaxIdleConns\")\n  final val DRIVER_CONNECTION_POOL_MIN_EVICTABLE_IDLE_TIME_MS = newOption(\n    \"driverConnectionPool.MinEvictableIdleTimeMS\")\n  final val DRIVER_CONNECTION_POOL_TIME_BETWEEN_EVICTION_RUNS_MS = newOption(\n    \"driverConnectionPool.TimeBetweenEvictionRunsMS\")\n  final val DRIVER_CONNECTION_POOL_MAX_WAIT_MS = newOption(\"driverConnectionPool.MaxWaitMS\")\n  final val DRIVER_CONNECTION_POOL_MAX_CONN_LIFETIME_MS = newOption(\n    \"driverConnectionPool.MaxConnLifetimeMS\")\n\n  final val EXECUTOR_CONNECTION_POOL_ENABLED = newOption(\"executorConnectionPool.Enabled\")\n  final val EXECUTOR_CONNECTION_POOL_MAX_OPEN_CONNS = newOption(\n    \"executorConnectionPool.MaxOpenConns\")\n  final val EXECUTOR_CONNECTION_POOL_MAX_IDLE_CONNS = newOption(\n    \"executorConnectionPool.MaxIdleConns\")\n  final val EXECUTOR_CONNECTION_POOL_MIN_EVICTABLE_IDLE_TIME_MS = newOption(\n    \"executorConnectionPool.MinEvictableIdleTimeMS\")\n  final val EXECUTOR_CONNECTION_POOL_TIME_BETWEEN_EVICTION_RUNS_MS = newOption(\n    \"executorConnectionPool.TimeBetweenEvictionRunsMS\")\n  final val EXECUTOR_CONNECTION_POOL_MAX_WAIT_MS = newOption(\"executorConnectionPool.MaxWaitMS\")\n  final val EXECUTOR_CONNECTION_POOL_MAX_CONN_LIFETIME_MS = newOption(\n    \"executorConnectionPool.MaxConnLifetimeMS\")\n\n  def getTable(options: CaseInsensitiveMap[String]): Option[TableIdentifier] =\n    options\n      .get(TABLE_NAME)\n      .orElse(options.get(PATH))\n      .map(CatalystSqlParser.parseTableIdentifier)\n\n  def getQuery(options: CaseInsensitiveMap[String]): String = {\n    val table = getTable(options)\n\n    require(\n      !(table.isDefined && options.isDefinedAt(QUERY)),\n      s\"The '$QUERY' option cannot be specified along with a table name.\"\n    )\n\n    options\n      .get(QUERY)\n      .orElse(table.map(t => s\"SELECT * FROM ${t.quotedString}\"))\n      .getOrElse(\n        throw new IllegalArgumentException(\n          s\"One of the following options must be specified: $QUERY, $TABLE_NAME, $PATH\"\n        )\n      )\n  }\n\n  def splitEscapedColumns(s: String): List[String] = {\n    def splitEscapedColumns(s: String, isQuoteOpen: Boolean): List[String] = {\n      if (s.isEmpty) {\n        List.empty\n      } else if (s.head == '`') {\n        val suffixRes = splitEscapedColumns(s.tail, !isQuoteOpen)\n        if (suffixRes.isEmpty) {\n          // it is the first column that we meet\n          // return list with one string\n          List(\"`\")\n        } else {\n          // prepend character to the first column\n          ('`' + suffixRes.head) +: suffixRes.tail\n        }\n      } else if (s.head == ',' && !isQuoteOpen) {\n        val res = splitEscapedColumns(s.tail, isQuoteOpen)\n        // we are processing a new column\n        // prepend empty column to the result\n        \"\" +: res\n      } else {\n        val res = splitEscapedColumns(s.tail, isQuoteOpen)\n        if (res.isEmpty) {\n          // it is the first column that we meet\n          // return list with one string\n          List(s.head.toString)\n        } else {\n          // prepend character to the first column\n          (s.head + res.head) +: res.tail\n        }\n      }\n    }\n\n    splitEscapedColumns(s, isQuoteOpen = false)\n  }\n\n  def trimAndUnescapeColumn(s: String): String = {\n    def unescapeColumn(s: List[Char]): List[Char] = {\n      if (s.isEmpty) {\n        List.empty\n      } else if (s.head == '`' && s.tail.nonEmpty && s.tail.head == '`') {\n        // we met escaped backtick\n        // append it only once\n        '`' +: unescapeColumn(s.tail.tail)\n      } else if (s.head == '`') {\n        // we met backtick\n        // skip it\n        unescapeColumn(s.tail)\n      } else {\n        // add a simple character\n        s.head +: unescapeColumn(s.tail)\n      }\n    }\n\n    val trimmed = s.trim\n    if (trimmed.nonEmpty && trimmed.head == '`') {\n      unescapeColumn(s.trim.toCharArray.toList).mkString(\"\")\n    } else {\n      trimmed\n    }\n  }\n\n  def apply(options: CaseInsensitiveMap[String], sc: SparkContext): SinglestoreOptions = {\n    val table = getTable(options)\n\n    val disablePushdown = options.get(DISABLE_PUSHDOWN).getOrElse(\"false\").toBoolean\n\n    require(\n      !(!disablePushdown && options.isDefinedAt(CUSTOM_SCHEMA)),\n      s\"Pushdown cannot be enabled when '$CUSTOM_SCHEMA' option is specified. Disable pushdown by setting '$DISABLE_PUSHDOWN' option to 'true'.\"\n    )\n\n    require(\n      options.isDefinedAt(DDL_ENDPOINT) || options.isDefinedAt(CLIENT_ENDPOINT),\n      s\"One of the following options must be specified: '$DDL_ENDPOINT', '$CLIENT_ENDPOINT'\"\n    )\n\n    require(\n      !(options.isDefinedAt(CLIENT_ENDPOINT) &&\n        (options.isDefinedAt(DDL_ENDPOINT) || options.isDefinedAt(DML_ENDPOINTS))),\n      s\"The '$CLIENT_ENDPOINT' option cannot be specified along with a '${DDL_ENDPOINT}' or '${DML_ENDPOINTS}'.\"\n    )\n\n    val loadDataCompression = CompressionType.fromOption(LOAD_DATA_COMPRESSION,\n                                                         options.get(LOAD_DATA_COMPRESSION),\n                                                         CompressionType.GZip)\n\n    // tableKeys are specified via options with the tableKey prefix\n    // the key is `tableKey.TYPE.NAME` where\n    //    TYPE is one of \"primary, unique, shard, index\"\n    //    NAME is the index name - must be unique\n    // the value is a column delimited list of columns for the index\n    val tableKeys = options\n      .filterKeys(_.toLowerCase.startsWith(TABLE_KEYS.toLowerCase + \".\"))\n      .map {\n        case (key, columns) => {\n          val keyParts = key.split(\"\\\\.\", 3)\n          if (keyParts.length < 2) {\n            sys.error(\n              s\"Options starting with '$TABLE_KEYS.' must be formatted correctly. The key should be in the form `$TABLE_KEYS.INDEX_TYPE[.NAME]`.\"\n            )\n          }\n          val keyType = TableKeyType\n            .fromString(keyParts(1))\n            .getOrElse(\n              sys.error(\n                s\"Option '$key' must specify an index type from the following options: ${TableKeyType.valuesString}\")\n            )\n          val keyName = keyParts.lift(2)\n          if (keyName.contains(\"\")) {\n            sys.error(s\"Option '$key' can not have an empty name\")\n          }\n          TableKey(keyType, keyName, columns)\n        }\n      }\n      .toList\n\n    val parallelReadMaxNumPartitions =\n      options.getOrElse(PARALLEL_READ_MAX_NUM_PARTITIONS, \"0\").toInt\n    val parallelReadNumPartitions = options.getOrElse(PARALLEL_READ_NUM_PARTITIONS, \"0\").toInt\n    if (parallelReadMaxNumPartitions != 0 && parallelReadNumPartitions > parallelReadMaxNumPartitions) {\n      sys.error(\n        s\"Option 'parallelRead.numPartitions' is greater then 'parallelRead.maxNumPartitions'\")\n    }\n\n    new SinglestoreOptions(\n      ddlEndpoint = options.getOrElse(DDL_ENDPOINT, options(CLIENT_ENDPOINT)),\n      dmlEndpoints = options\n        .getOrElse(DML_ENDPOINTS, options.getOrElse(DDL_ENDPOINT, options(CLIENT_ENDPOINT)))\n        .split(\",\")\n        .toList\n        .sorted,\n      user = options.getOrElse(USER, \"root\"),\n      password = options.getOrElse(PASSWORD, \"\"),\n      database = options.get(DATABASE).orElse(table.flatMap(t => t.database)),\n      jdbcExtraOptions = options.originalMap\n        .filter { case (key, _) => !singlestoreOptionNames(key.toLowerCase()) },\n      enableAsserts = options.get(ENABLE_ASSERTS).getOrElse(\"false\").toBoolean,\n      disablePushdown = disablePushdown,\n      customSchema = options.get(CUSTOM_SCHEMA).map(StructType.fromDDL),\n      enableParallelRead =\n        ParallelReadEnablement(options.get(ENABLE_PARALLEL_READ).getOrElse(\"automaticLite\")),\n      overwriteBehavior = {\n        val truncateOption          = options.get(TRUNCATE)\n        val overwriteBehaviorOption = options.get(OVERWRITE_BEHAVIOR)\n        if (truncateOption.isDefined && overwriteBehaviorOption.isDefined) {\n          throw new IllegalArgumentException(\n            s\"can't use both `$TRUNCATE` and `$OVERWRITE_BEHAVIOR` options, please use just `$OVERWRITE_BEHAVIOR` option instead.\")\n        }\n        if (truncateOption.getOrElse(\"false\").toBoolean) {\n          log.warn(\n            s\"`$TRUNCATE` option is deprecated, please use the `$OVERWRITE_BEHAVIOR` option instead.\")\n          Truncate\n        } else {\n          /* DropAndCreate is the default behaviour if another isn't defined */\n          overwriteBehaviorOption\n            .fold[OverwriteBehavior](DropAndCreate)(OverwriteBehavior(_))\n        }\n      },\n      loadDataCompression = loadDataCompression,\n      loadDataFormat = LoadDataFormat.fromOption(LOAD_DATA_FORMAT,\n                                                 options.get(LOAD_DATA_FORMAT),\n                                                 LoadDataFormat.CSV),\n      tableKeys = tableKeys,\n      onDuplicateKeySQL = options.get(ON_DUPLICATE_KEY_SQL),\n      insertBatchSize = options.get(INSERT_BATCH_SIZE).getOrElse(\"10000\").toInt,\n      maxErrors = {\n        val onDuplicateKeySqlOption = options.get(ON_DUPLICATE_KEY_SQL)\n        val maxErrorsOption         = options.get(MAX_ERRORS)\n        if (maxErrorsOption.isDefined && onDuplicateKeySqlOption.isDefined) {\n          throw new IllegalArgumentException(\n            s\"can't use both `$ON_DUPLICATE_KEY_SQL` and `$MAX_ERRORS` options\")\n        } else {\n          maxErrorsOption.getOrElse(\"0\").toInt\n        }\n      },\n      parallelReadFeatures = {\n        options\n          .get(PARALLEL_READ_FEATURES)\n          .getOrElse(\"ReadFromAggregators\")\n          .split(\",\")\n          .map(feature => ParallelReadType(feature.trim))\n          .toList\n      },\n      parallelReadTableCreationTimeoutMS = {\n        options.getOrElse(PARALLEL_READ_TABLE_CREATION_TIMEOUT_MS, \"0\").toInt\n      },\n      parallelReadMaterializedTableCreationTimeoutMS = {\n        options.getOrElse(PARALLEL_READ_MATERIALIZED_TABLE_CREATION_TIMEOUT_MS, \"0\").toInt\n      },\n      parallelReadMaxNumPartitions = parallelReadMaxNumPartitions,\n      parallelReadNumPartitions = parallelReadNumPartitions,\n      parallelReadRepartition = options.get(PARALLEL_READ_REPARTITION).getOrElse(\"false\").toBoolean,\n      parallelReadRepartitionColumns =\n        splitEscapedColumns(options.get(PARALLEL_READ_REPARTITION_COLUMNS).getOrElse(\"\"))\n          .map(column => trimAndUnescapeColumn(column))\n          .toSet,\n      createRowstoreTable = options.getOrElse(CREATE_ROWSTORE_TABLE, \"false\").toBoolean,\n      executorConnectionPoolOptions = SinglestoreConnectionPoolOptions(\n        options.getOrElse(EXECUTOR_CONNECTION_POOL_ENABLED, \"true\").toBoolean,\n        options.getOrElse(EXECUTOR_CONNECTION_POOL_MAX_OPEN_CONNS, \"-1\").toInt,\n        options.getOrElse(EXECUTOR_CONNECTION_POOL_MAX_IDLE_CONNS, \"8\").toInt,\n        options.getOrElse(EXECUTOR_CONNECTION_POOL_MIN_EVICTABLE_IDLE_TIME_MS, \"30000\").toLong,\n        options.getOrElse(EXECUTOR_CONNECTION_POOL_TIME_BETWEEN_EVICTION_RUNS_MS, \"1000\").toLong,\n        options.getOrElse(EXECUTOR_CONNECTION_POOL_MAX_WAIT_MS, \"-1\").toLong,\n        options.getOrElse(EXECUTOR_CONNECTION_POOL_MAX_CONN_LIFETIME_MS, \"-1\").toLong,\n      ),\n      driverConnectionPoolOptions = SinglestoreConnectionPoolOptions(\n        options.getOrElse(DRIVER_CONNECTION_POOL_ENABLED, \"true\").toBoolean,\n        options.getOrElse(DRIVER_CONNECTION_POOL_MAX_OPEN_CONNS, \"-1\").toInt,\n        options.getOrElse(DRIVER_CONNECTION_POOL_MAX_IDLE_CONNS, \"8\").toInt,\n        options.getOrElse(DRIVER_CONNECTION_POOL_MIN_EVICTABLE_IDLE_TIME_MS, \"2000\").toLong,\n        options.getOrElse(DRIVER_CONNECTION_POOL_TIME_BETWEEN_EVICTION_RUNS_MS, \"1000\").toLong,\n        options.getOrElse(DRIVER_CONNECTION_POOL_MAX_WAIT_MS, \"-1\").toLong,\n        options.getOrElse(DRIVER_CONNECTION_POOL_MAX_CONN_LIFETIME_MS, \"-1\").toLong,\n      ),\n      sparkVersion = sc.version\n    )\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestorePartitioner.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.SQLException\nimport java.util.Properties\n\nimport com.singlestore.spark.JdbcHelpers.{getDDLConnProperties, getDMLConnProperties}\nimport com.singlestore.spark.SQLGen.{SinglestoreVersion, VariableList}\nimport org.apache.spark.scheduler.MaxNumConcurrentTasks\nimport org.apache.spark.Partition\nimport spray.json.DeserializationException\nimport spray.json.DefaultJsonProtocol._\nimport spray.json._\n\nimport scala.util.Try\n\n// SinglestoreMultiPartition represents single Spark partition reading from >= 1 SingleStore partitions\ncase class SinglestoreMultiPartition(override val index: Int, partitions: Seq[SinglestorePartition])\n    extends Partition\n\ncase class SinglestorePartition(\n    override val index: Int,\n    query: String,\n    variables: VariableList,\n    connectionInfo: Properties,\n) extends Partition\n\ncase class SinglestorePartitioner(rdd: SinglestoreRDD) extends LazyLogging {\n  private val options: SinglestoreOptions = rdd.options\n\n  private var errorMessages: Map[ParallelReadType, String] = Map.empty\n\n  private val executorWhitelist: Set[String] = Set(\n    \"Project\",\n    \"Gather\",\n    \"Filter\",\n    \"TableScan\",\n    \"ColumnStoreScan\",\n    \"ColumnStoreFilter\",\n    \"OrderedColumnStoreScan\",\n    \"IndexRangeScan\",\n    \"IndexSeek\",\n    \"NestedLoopJoin\",\n    \"ChoosePlan\",\n    \"HashGroupBy\",\n    \"StreamingGroupBy\"\n  ).map(_.toLowerCase)\n\n  private def coalescePartitions(partitions: Seq[SinglestorePartition],\n                                 numPartitions: Int): Array[Partition] = {\n    partitions\n      .groupBy(p => p.index % numPartitions)\n      .toArray\n      .sortBy(p => p._1)\n      .map(p => SinglestoreMultiPartition(p._1, p._2))\n  }\n\n  def saveErrorMessage(parallelReadType: ParallelReadType, message: String): Unit = {\n    errorMessages += (parallelReadType -> message)\n    if (log.isTraceEnabled()) {\n      log.trace(s\"$parallelReadType disabled for this query: $message\")\n    }\n  }\n\n  private def partitionsFromExplainJSON(database: String,\n                                        partitionHostPorts: List[SinglestorePartitionInfo],\n                                        explainJSON: JsValue): Option[Array[Partition]] = {\n    def saveErrorMessageReadFromLeaves(message: String): Unit = {\n      saveErrorMessage(ReadFromLeaves, message)\n    }\n\n    // The top level Explain is either the explain tree or a \"metadata\" wrapper\n    // (we can disambiguate between these cases by checking for the presence of the \"version\" key)\n\n    var root       = explainJSON\n    val rootFields = explainJSON.asJsObject().fields\n    if (rootFields.contains(\"version\")) {\n      // In explain version 2+ (introduced in SingleStore 6.7), the output\n      // we want is nested under the \"explain\" key as the only element of an array\n      root = rootFields(\"explain\").convertTo[List[JsObject]].head\n    }\n\n    // Collect required execution metadata\n    // We need to walk through the tree and collect the \"executor\" and \"query\" fields from each node\n\n    def walk(node: JsValue): Seq[(Option[String], Option[String])] = {\n      val fields   = node.asJsObject.fields\n      val executor = fields.get(\"executor\").map(_.convertTo[String].toLowerCase)\n      val query = fields\n        .get(\"query\")\n        .map(_.convertTo[String])\n      val children =\n        fields.get(\"inputs\").map(_.convertTo[Seq[JsValue]].flatMap(walk)).getOrElse(Nil)\n      Seq((executor, query)) ++ children\n    }\n\n    val (maybeExecutors, maybeQueries) = walk(root).unzip\n    val executors                      = maybeExecutors.flatten\n    val queries                        = maybeQueries.flatten\n\n    // We are able to pushdown when the following conditions hold:\n    // 1. there is exactly one query\n    if (queries.length != 1) {\n      saveErrorMessageReadFromLeaves(\n        s\"SingleStore would run more than one query during execution (${queries.length} queries found)\")\n      return None\n    }\n\n    // 2. all of the executors are in our whitelist\n    if (executors.map(!executorWhitelist.contains(_)).exists(identity)) {\n      saveErrorMessageReadFromLeaves(\n        s\"SingleStore is using parallel-unsafe executors (distinct executors in use: ${executors.toSet\n          .mkString(\", \")})\")\n      return None\n    }\n\n    // 3. there is only one gather, and it is the first executor\n    val numGathers  = executors.count(_ == \"gather\")\n    val gatherFirst = executors.headOption.contains(\"gather\")\n    if (numGathers != 1 || !gatherFirst) {\n      saveErrorMessageReadFromLeaves(\n        s\"the gather method used by this query is not supported ($numGathers, $gatherFirst)\")\n      return None\n    }\n\n    // 4. all leaves are connectable\n    if (partitionHostPorts.exists(p =>\n          Try {\n            SinglestoreConnectionPool\n              .getConnection(\n                JdbcHelpers.getConnProperties(options, isOnExecutor = false, p.hostport))\n              .close()\n          }.isFailure)) {\n      saveErrorMessageReadFromLeaves(s\"some leaves are not connectable\")\n      return None\n    }\n\n    // Success! we can do partition pushdown, to do this we need to generate queries for each partition\n\n    // we checked earlier that queries.length == 1, so we can safely grab the first query here\n    var partitionQuery = queries.head\n    // the partitionQuery may start with USING, so lets remove everything up to the first SELECT\n    partitionQuery = partitionQuery.slice(partitionQuery.indexOf(\"SELECT\"), partitionQuery.length)\n\n    // Starting from 9.0.0 SingleStore fails when trying to execute a query with SELECT WITH(PARALLELISM_LEVEL=\"SEGMENT\") syntax on the leaf nodes\n    partitionQuery =\n      partitionQuery.replaceFirst(\"\"\"^SELECT WITH\\(PARALLELISM_LEVEL=\"SEGMENT\"\\)\"\"\", \"SELECT\")\n\n    val firstPartitionName = s\"${database}_0\"\n\n    val singlePartitions = partitionHostPorts\n      .map(\n        p =>\n          SinglestorePartition(\n            p.ordinal,\n            partitionQuery.replace(firstPartitionName, p.name),\n            // SingleStore has already injected our variables into the query\n            // so we don't have to do any additional injection\n            Nil,\n            JdbcHelpers.getConnProperties(options, isOnExecutor = true, p.hostport)\n        ))\n\n    val partitionsNum =\n      if (options.parallelReadNumPartitions > 0) {\n        singlePartitions.length.min(options.parallelReadNumPartitions)\n      } else if (options.parallelReadMaxNumPartitions > 0) {\n        singlePartitions.length.min(options.parallelReadMaxNumPartitions)\n      } else {\n        singlePartitions.length\n      }\n\n    Some(coalescePartitions(singlePartitions, partitionsNum))\n  }\n\n  private lazy val databasePartitionCount: Int = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDMLConnProperties(options, isOnExecutor = false))\n    try {\n      JdbcHelpers.getPartitionsCount(conn, options.database.get)\n    } finally {\n      conn.close()\n    }\n  }\n\n  private def aggregatorReadPartitions(partitionsNum: Int): Some[Array[Partition]] = {\n    val singlePartitions = Seq\n      .range(0, databasePartitionCount)\n      .map(\n        index =>\n          SinglestorePartition(index,\n                               rdd.query,\n                               rdd.variables,\n                               getDMLConnProperties(options, isOnExecutor = true)))\n\n    Some(coalescePartitions(singlePartitions, partitionsNum))\n  }\n\n  private lazy val readFromLeavesPartitions: Option[Array[Partition]] = {\n    val minimalExternalHostVersion = \"7.1.0\"\n    val explainJSON =\n      JdbcHelpers.explainJSONQuery(options, rdd.query, rdd.variables).parseJson\n    val partitions = JdbcHelpers.partitionHostPorts(options, options.database.head)\n    val partitionHostPorts = {\n      val singlestoreVersion = SinglestoreVersion(JdbcHelpers.getSinglestoreVersion(options))\n      if (singlestoreVersion.atLeast(minimalExternalHostVersion)) {\n        val externalHostMap = JdbcHelpers.externalHostPorts(options)\n        var isValid         = true\n        val externalPartitions = partitions.flatMap(p => {\n          val externalHost = externalHostMap.get(p.hostport)\n          if (externalHost.isDefined) {\n            Some(SinglestorePartitionInfo(p.ordinal, p.name, externalHost.get))\n          } else {\n            isValid = false\n            None\n          }\n        })\n        if (isValid) externalPartitions\n        else partitions\n      } else {\n        partitions\n      }\n    }\n    try {\n      partitionsFromExplainJSON(options.database.head, partitionHostPorts, explainJSON)\n    } catch {\n      case _: DeserializationException => None\n    }\n  }\n\n  private lazy val readFromAggregatorsMaterializedPartitions: Option[Array[Partition]] = {\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(options, isOnExecutor = true))\n\n    if (!JdbcHelpers.isValidQuery(\n          conn,\n          JdbcHelpers.getCreateResultTableQuery(\n            \"CheckIfSelectQueryIsSupportedByParallelRead\",\n            rdd.query,\n            rdd.schema,\n            materialized = true,\n            needsRepartition = rdd.options.parallelReadRepartition,\n            rdd.parallelReadRepartitionColumns\n          ),\n          rdd.variables\n        )) {\n      saveErrorMessage(ReadFromAggregatorsMaterialized,\n                       \"the query is not supported by aggregator parallel read\")\n      None\n    } else {\n      val numPartitions = if (options.parallelReadNumPartitions > 0) {\n        databasePartitionCount.min(options.parallelReadNumPartitions)\n      } else if (options.parallelReadMaxNumPartitions > 0) {\n        databasePartitionCount.min(options.parallelReadMaxNumPartitions)\n      } else {\n        databasePartitionCount\n      }\n\n      aggregatorReadPartitions(numPartitions)\n    }\n  }\n\n  private lazy val readFromAggregatorsPartitions: Option[Array[Partition]] = {\n    def saveErrorMessageReadFromAggregators(message: String): Unit = {\n      saveErrorMessage(ReadFromAggregators, message)\n    }\n\n    val conn =\n      SinglestoreConnectionPool.getConnection(getDDLConnProperties(options, isOnExecutor = true))\n    val maxNumConcurrentTasks = MaxNumConcurrentTasks.get(rdd)\n\n    if (!JdbcHelpers.isValidQuery(\n          conn,\n          JdbcHelpers.getCreateResultTableQuery(\n            \"CheckIfSelectQueryIsSupportedByParallelRead\",\n            rdd.query,\n            rdd.schema,\n            materialized = false,\n            needsRepartition = rdd.options.parallelReadRepartition,\n            rdd.parallelReadRepartitionColumns\n          ),\n          rdd.variables\n        )) {\n      saveErrorMessageReadFromAggregators(\"the query is not supported by aggregator parallel read\")\n      None\n    } else if (options.parallelReadNumPartitions == 0 && maxNumConcurrentTasks == 0) {\n      saveErrorMessageReadFromAggregators(\n        \"failed to retrieve maximum number of concurrent tasks and number of partitions is not specified.\\n\" +\n          \"Try to set `parallelRead.numPartitions` parameter.\")\n      None\n    } else {\n      val numPartitions = if (options.parallelReadNumPartitions > 0) {\n        databasePartitionCount\n          .min(options.parallelReadNumPartitions)\n      } else if (options.parallelReadMaxNumPartitions > 0) {\n        databasePartitionCount\n          .min(MaxNumConcurrentTasks.get(rdd))\n          .min(options.parallelReadMaxNumPartitions)\n      } else {\n        databasePartitionCount\n          .min(MaxNumConcurrentTasks.get(rdd))\n      }\n\n      aggregatorReadPartitions(numPartitions)\n    }\n  }\n\n  private lazy val nonParallelReadPartitions: Option[Array[Partition]] =\n    Some(\n      coalescePartitions(\n        Seq(\n          SinglestorePartition(0,\n                               rdd.query,\n                               rdd.variables,\n                               getDMLConnProperties(options, isOnExecutor = true))),\n        1))\n\n  private def getPartitions(parallelReadType: ParallelReadType): Option[Array[Partition]] =\n    if (options.database.isEmpty) {\n      None\n    } else {\n      parallelReadType match {\n        case ReadFromLeaves                  => readFromLeavesPartitions\n        case ReadFromAggregators             => readFromAggregatorsPartitions\n        case ReadFromAggregatorsMaterialized => readFromAggregatorsMaterializedPartitions\n        case _                               => None\n      }\n    }\n\n  def getPartitions: (Option[ParallelReadType], Array[Partition]) = {\n    val readType = options.enableParallelRead match {\n      case Disabled =>\n        None\n      case AutomaticLite if rdd.resultMustBeSorted =>\n        None\n      case _ =>\n        rdd.options.parallelReadFeatures\n          .collectFirst { case readType if getPartitions(readType).isDefined => readType }\n    }\n\n    if (readType.isEmpty && options.enableParallelRead == Forced) {\n      throw new ParallelReadFailedException(errorMessages)\n    }\n\n    val partitions = readType match {\n      case None                                  => nonParallelReadPartitions\n      case Some(ReadFromLeaves)                  => readFromLeavesPartitions\n      case Some(ReadFromAggregators)             => readFromAggregatorsPartitions\n      case Some(ReadFromAggregatorsMaterialized) => readFromAggregatorsMaterializedPartitions\n    }\n\n    (readType, partitions.get)\n  }\n}\n\nfinal class ParallelReadFailedException(errors: Map[ParallelReadType, String])\n    extends SQLException(\n      s\"Failed to read data in parallel.\\n\" +\n        s\"Tried following parallel read features:\\n\" +\n        s\"${errors\n          .map(error => s\" * ${error._1} - ${error._2}\\n\")\n          .mkString(\"\")}\")\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreRDD.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Connection, Date, PreparedStatement, ResultSet}\nimport java.util.concurrent.{Executors, ForkJoinPool}\nimport com.singlestore.spark.SQLGen.VariableList\nimport org.apache.spark.rdd.RDD\nimport org.apache.spark.sql.Row\nimport org.apache.spark.sql.catalyst.expressions.Attribute\nimport org.apache.spark.sql.execution.datasources.jdbc.JdbcUtils\nimport org.apache.spark.sql.types._\nimport org.apache.spark.util.TaskCompletionListener\nimport org.apache.spark.{InterruptibleIterator, Partition, SparkContext, TaskContext}\n\nimport scala.concurrent.duration.Duration\nimport scala.concurrent.ExecutionContext.Implicits.global\nimport scala.concurrent.{Await, ExecutionContext, Future}\n\ncase class SinglestoreRDD(query: String,\n                          variables: VariableList,\n                          options: SinglestoreOptions,\n                          schema: StructType,\n                          expectedOutput: Seq[Attribute],\n                          resultMustBeSorted: Boolean,\n                          parallelReadRepartitionColumns: Seq[String],\n                          @transient val sc: SparkContext,\n                          randHex: String)\n    extends RDD[Row](sc, Nil) {\n  val (parallelReadType, partitions_) = SinglestorePartitioner(this).getPartitions\n\n  // Spark serializes RDD object and sends it to executor\n  // On executor sc value will be null as it is marked as transient\n  def isRunOnExecutor: Boolean = sc == null\n\n  val applicationId: String = sc.applicationId\n\n  val aggregatorParallelReadUsed: Boolean =\n    parallelReadType.contains(ReadFromAggregators) ||\n      parallelReadType.contains(ReadFromAggregatorsMaterialized)\n\n  if (!isRunOnExecutor && aggregatorParallelReadUsed) {\n    AggregatorParallelReadListenerAdder.addRDD(this)\n  }\n\n  override def finalize(): Unit = {\n    if (!isRunOnExecutor && aggregatorParallelReadUsed) {\n      AggregatorParallelReadListenerAdder.deleteRDD(this)\n    }\n    super.finalize()\n  }\n\n  override protected def getPartitions: Array[Partition] = partitions_\n\n  override def compute(rawPartition: Partition, context: TaskContext): Iterator[Row] = {\n    val multiPartition: SinglestoreMultiPartition =\n      rawPartition.asInstanceOf[SinglestoreMultiPartition]\n    val threadPool = new ForkJoinPool(multiPartition.partitions.size)\n    try {\n      val executionContext =\n        ExecutionContext.fromExecutor(threadPool)\n      val future: Future[Seq[Iterator[Row]]] = Future.sequence(multiPartition.partitions.map(p =>\n        Future(computeSinglePartition(p, context))(executionContext)))\n\n      Await.result(future, Duration.Inf).foldLeft(Iterator[Row]())(_ ++ _)\n    } finally {\n      threadPool.shutdownNow()\n    }\n  }\n\n  def computeSinglePartition(rawPartition: SinglestorePartition,\n                             context: TaskContext): Iterator[Row] = {\n    var closed                          = false\n    var rs: ResultSet                   = null\n    var stmt: PreparedStatement         = null\n    var conn: Connection                = null\n    val partition: SinglestorePartition = rawPartition.asInstanceOf[SinglestorePartition]\n\n    def tryClose(name: String, what: AutoCloseable): Unit = {\n      try {\n        if (what != null) { what.close() }\n      } catch {\n        case e: Exception => logWarning(s\"Exception closing $name\", e)\n      }\n    }\n\n    val ErrResultTableNotExistCode = 2318\n\n    def close(): Unit = {\n      if (closed) { return }\n      tryClose(\"resultset\", rs)\n      tryClose(\"statement\", stmt)\n      tryClose(\"connection\", conn)\n      closed = true\n    }\n\n    context.addTaskCompletionListener {\n      new TaskCompletionListener {\n        override def onTaskCompletion(context: TaskContext): Unit = close()\n      }\n    }\n\n    conn = SinglestoreConnectionPool.getConnection(partition.connectionInfo)\n    if (aggregatorParallelReadUsed) {\n      val tableName = JdbcHelpers.getResultTableName(applicationId,\n                                                     context.stageId(),\n                                                     id,\n                                                     context.stageAttemptNumber(),\n                                                     randHex)\n\n      stmt =\n        conn.prepareStatement(JdbcHelpers.getSelectFromResultTableQuery(tableName, partition.index))\n\n      val startTime = System.currentTimeMillis()\n      val timeout = parallelReadType match {\n        case Some(ReadFromAggregators) =>\n          options.parallelReadTableCreationTimeoutMS\n        case Some(ReadFromAggregatorsMaterialized) =>\n          options.parallelReadMaterializedTableCreationTimeoutMS\n        case _ =>\n          0\n      }\n\n      var lastError: java.sql.SQLException = null\n      var delay                            = 50\n      val maxDelay                         = 10000\n      while (rs == null && (timeout == 0 || System.currentTimeMillis() - startTime < timeout)) {\n        try {\n          rs = stmt.executeQuery()\n        } catch {\n          case e: java.sql.SQLException if e.getErrorCode == ErrResultTableNotExistCode =>\n            lastError = e\n            delay = Math.min(maxDelay, delay * 2)\n            Thread.sleep(delay)\n        }\n      }\n\n      if (rs == null) {\n        throw new java.sql.SQLException(\"Failed to read data from result table\", lastError)\n      }\n    } else {\n      stmt = conn.prepareStatement(partition.query)\n      JdbcHelpers.fillStatement(stmt, partition.variables)\n      rs = stmt.executeQuery()\n    }\n\n    var rowsIter = JdbcUtils.resultSetToRows(rs, schema)\n\n    if (expectedOutput.nonEmpty) {\n      val schemaDatatypes   = schema.map(_.dataType)\n      val expectedDatatypes = expectedOutput.map(_.dataType)\n\n      def getOrNull(f: => Any, r: Row, i: Int): Any = {\n        if (r.isNullAt(i)) null\n        else f\n      }\n\n      if (schemaDatatypes != expectedDatatypes) {\n        val columnEncoders = schemaDatatypes.zip(expectedDatatypes).zipWithIndex.map {\n          case ((_: StringType, _: NullType), _) => ((_: Row) => null)\n          case ((_: ShortType, _: BooleanType), i) =>\n            (r: Row) =>\n              getOrNull(r.getShort(i) != 0, r, i)\n          case ((_: IntegerType, _: BooleanType), i) =>\n            (r: Row) =>\n              getOrNull(r.getInt(i) != 0, r, i)\n          case ((_: LongType, _: BooleanType), i) =>\n            (r: Row) =>\n              getOrNull(r.getLong(i) != 0, r, i)\n\n          case ((_: ShortType, _: ByteType), i) =>\n            (r: Row) =>\n              getOrNull(r.getShort(i).toByte, r, i)\n          case ((_: IntegerType, _: ByteType), i) =>\n            (r: Row) =>\n              getOrNull(r.getInt(i).toByte, r, i)\n          case ((_: LongType, _: ByteType), i) =>\n            (r: Row) =>\n              getOrNull(r.getLong(i).toByte, r, i)\n          case ((_: LongType, _: IntegerType), i) =>\n            (r: Row) =>\n              getOrNull(r.getLong(i).toInt, r, i)\n          case ((_: DecimalType, _: DoubleType), i) =>\n            (r: Row) =>\n              getOrNull(r.getDecimal(i).doubleValue(), r, i)\n          case ((_: StringType, _: DateType), i) =>\n            (r: Row) =>\n              getOrNull(Date.valueOf(r.getString(i)), r, i)\n\n          case ((l, r), i) =>\n            options.assert(l == r, s\"SinglestoreRDD: unable to encode ${l} into ${r}\")\n            ((r: Row) => getOrNull(r.get(i), r, i))\n        }\n\n        rowsIter = rowsIter\n          .map(row => Row.fromSeq(columnEncoders.map(_(row))))\n      }\n    }\n\n    CompletionIterator[Row, Iterator[Row]](new InterruptibleIterator[Row](context, rowsIter), close)\n  }\n\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/SinglestoreReader.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.SQLSyntaxErrorException\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, VariableList}\nimport org.apache.spark.rdd.RDD\nimport org.apache.spark.sql.catalyst.expressions.{Attribute, Expression => CatalystExpression}\nimport org.apache.spark.sql.sources.{BaseRelation, CatalystScan, TableScan}\nimport org.apache.spark.sql.{Row, SQLContext}\n\nimport scala.util.Random;\n\ncase class SinglestoreReaderNoPushdown(query: String,\n                                       options: SinglestoreOptions,\n                                       @transient val sqlContext: SQLContext)\n    extends BaseRelation\n    with TableScan {\n\n  override lazy val schema = JdbcHelpers.loadSchema(options, query, Nil)\n\n  override def buildScan: RDD[Row] = {\n    val randHex = Random.nextInt().toHexString\n    val rdd =\n      SinglestoreRDD(\n        query,\n        Nil,\n        options,\n        schema,\n        Nil,\n        resultMustBeSorted = false,\n        schema\n          .filter(sf => options.parallelReadRepartitionColumns.contains(sf.name))\n          .map(sf => SQLGen.Ident(sf.name).sql),\n        sqlContext.sparkContext,\n        randHex\n      )\n      // Add random hex to the name\n      // It is needed to generate unique names for result tables during parallel read\n      .setName(\"SingleStoreRDD\" + randHex)\n    if (rdd.parallelReadType.contains(ReadFromAggregators)) {\n      // Wrap an RDD with barrier stage, to force all readers start reading at the same time.\n      // Repartition it to force spark to read data and do all other computations in different stages.\n      // Otherwise we will likely get the following error:\n      // [SPARK-24820][SPARK-24821]: Barrier execution mode does not allow the following\n      // pattern of RDD chain within a barrier stage...\n      rdd.barrier().mapPartitions(v => v).repartition(rdd.getNumPartitions)\n    } else {\n      rdd\n    }\n  }\n}\n\ncase class SinglestoreReader(query: String,\n                             variables: VariableList,\n                             options: SinglestoreOptions,\n                             @transient val sqlContext: SQLContext,\n                             isFinal: Boolean = false,\n                             expectedOutput: Seq[Attribute] = Nil,\n                             var resultMustBeSorted: Boolean = false,\n                             context: SQLGenContext)\n    extends BaseRelation\n    with LazyLogging\n    with TableScan\n    with CatalystScan {\n\n  override lazy val schema = JdbcHelpers.loadSchema(options, query, variables)\n\n  override def buildScan: RDD[Row] = {\n    val randHex = Random.nextInt().toHexString\n    val rdd =\n      SinglestoreRDD(\n        query,\n        variables,\n        options,\n        schema,\n        expectedOutput,\n        resultMustBeSorted,\n        expectedOutput\n          .filter(attr => options.parallelReadRepartitionColumns.contains(attr.name))\n          .map(attr => context.ident(attr.name, attr.exprId)),\n        sqlContext.sparkContext,\n        randHex\n      )     \n      // Add random hex to the name\n      // It is needed to generate unique names for result tables during parallel read\n      .setName(\"SingleStoreRDD\" + randHex)\n    if (rdd.parallelReadType.contains(ReadFromAggregators)) {\n      // Wrap an RDD with barrier stage, to force all readers start reading at the same time.\n      // Repartition it to force spark to read data and do all other computations in different stages.\n      // Otherwise we will likely get the following error:\n      // [SPARK-24820][SPARK-24821]: Barrier execution mode does not allow the following\n      // pattern of RDD chain within a barrier stage...\n      rdd.barrier().mapPartitions(v => v).repartition(rdd.getNumPartitions)\n    } else {\n      rdd\n    }\n  }\n\n  override def buildScan(rawColumns: Seq[Attribute],\n                         rawFilters: Seq[CatalystExpression]): RDD[Row] = {\n    // we don't have to push down *everything* using this interface since Spark will\n    // run the projection and filter again upon receiving the results from SingleStore\n    val projection =\n      rawColumns\n        .flatMap(ExpressionGen.apply(ExpressionExtractor(context)).lift(_))\n        .reduceOption(_ + \",\" + _)\n    val filters =\n      rawFilters\n        .flatMap(ExpressionGen.apply(ExpressionExtractor(context)).lift(_))\n        .reduceOption(_ + \"AND\" + _)\n\n    val stmt = (projection, filters) match {\n      case (Some(p), Some(f)) =>\n        SQLGen\n          .select(p)\n          .from(SQLGen.Relation(Nil, this, context.nextAlias(), null))\n          .where(f)\n          .output(rawColumns)\n      case (Some(p), None) =>\n        SQLGen\n          .select(p)\n          .from(SQLGen.Relation(Nil, this, context.nextAlias(), null))\n          .output(rawColumns)\n      case (None, Some(f)) =>\n        SQLGen.selectAll\n          .from(SQLGen.Relation(Nil, this, context.nextAlias(), null))\n          .where(f)\n          .output(expectedOutput)\n      case _ =>\n        return buildScan\n    }\n\n    val newReader = copy(query = stmt.sql, variables = stmt.variables, expectedOutput = stmt.output)\n\n    if (log.isTraceEnabled) {\n      log.trace(s\"CatalystScan additional rewrite:\\n${newReader}\")\n    }\n\n    newReader.buildScan\n  }\n\n  override def toString: String = {\n    val explain =\n      try {\n        JdbcHelpers.explainQuery(options, query, variables)\n      } catch {\n        case e: SQLSyntaxErrorException => e.toString\n        case e: Exception               => throw e\n      }\n    val v = variables.map(_.variable).mkString(\", \")\n\n    s\"\"\"\n      |---------------\n      |SingleStore Query\n      |Variables: ($v)\n      |SQL:\n      |$query\n      |\n      |EXPLAIN:\n      |$explain\n      |---------------\n      \"\"\".stripMargin\n  }\n}\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/vendor/apache/SchemaConverters.scala",
    "content": "package com.singlestore.spark.vendor.apache\n\nimport org.apache.avro._\nimport org.apache.spark.sql.types._\n\n/**\n  *  NOTE: this converter has been taken from `spark-avro` library,\n  *  as this functionality starts with spark 2.4.0 but we support lower versions.\n  *  org.apache.spark.sql.avro.SchemaConverters  (2.4.0 version)\n  *   Changes:\n  *   1. Removed everything except toAvroType function\n  */\nobject SchemaConverters {\n\n  private lazy val nullSchema = Schema.create(Schema.Type.NULL)\n\n  case class SchemaType(dataType: DataType, nullable: Boolean)\n\n  def toAvroType(catalystType: DataType,\n                 nullable: Boolean = false,\n                 recordName: String = \"topLevelRecord\",\n                 nameSpace: String = \"\"): Schema = {\n    val builder = SchemaBuilder.builder()\n\n    val schema = catalystType match {\n      case BooleanType                        => builder.booleanType()\n      case ByteType | ShortType | IntegerType => builder.intType()\n      case LongType                           => builder.longType()\n      case DateType =>\n        LogicalTypes.date().addToSchema(builder.intType())\n      case TimestampType =>\n        LogicalTypes.timestampMicros().addToSchema(builder.longType())\n\n      case FloatType      => builder.floatType()\n      case DoubleType     => builder.doubleType()\n      case StringType     => builder.stringType()\n      case _: DecimalType => builder.stringType()\n      case BinaryType     => builder.bytesType()\n      case ArrayType(et, containsNull) =>\n        builder\n          .array()\n          .items(toAvroType(et, containsNull, recordName, nameSpace))\n      case MapType(StringType, vt, valueContainsNull) =>\n        builder\n          .map()\n          .values(toAvroType(vt, valueContainsNull, recordName, nameSpace))\n      case st: StructType =>\n        val childNameSpace  = if (nameSpace != \"\") s\"$nameSpace.$recordName\" else recordName\n        val fieldsAssembler = builder.record(recordName).namespace(nameSpace).fields()\n        st.foreach { f =>\n          val fieldAvroType =\n            toAvroType(f.dataType, f.nullable, f.name, childNameSpace)\n          fieldsAssembler.name(f.name).`type`(fieldAvroType).noDefault()\n        }\n        fieldsAssembler.endRecord()\n\n      // This should never happen.\n      case other => throw new IncompatibleSchemaException(s\"Unexpected type $other.\")\n    }\n    if (nullable) {\n      Schema.createUnion(schema, nullSchema)\n    } else {\n      schema\n    }\n  }\n}\n\nclass IncompatibleSchemaException(msg: String, ex: Throwable = null) extends Exception(msg, ex)\n"
  },
  {
    "path": "src/main/scala/com/singlestore/spark/vendor/apache/third_party_license",
    "content": "                                Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/MaxNumConcurentTasks.scala",
    "content": "package org.apache.spark.scheduler\n\nimport org.apache.spark.rdd.RDD\n\nobject MaxNumConcurrentTasks {\n  def get(rdd: RDD[_]): Int = {\n    val (_, resourceProfiles) =\n      rdd.sparkContext.dagScheduler.getShuffleDependenciesAndResourceProfiles(rdd)\n    val resourceProfile =\n      rdd.sparkContext.dagScheduler.mergeResourceProfilesForStage(resourceProfiles)\n    rdd.sparkContext.maxNumConcurrentTasks(resourceProfile)\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/VersionSpecificAggregateExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport com.singlestore.spark.ExpressionGen.{aggregateWithFilter, f, op}\nimport org.apache.spark.sql.catalyst.expressions.aggregate.{\n  AggregateFunction,\n  Average,\n  First,\n  Kurtosis,\n  Last,\n  Skewness,\n  StddevPop,\n  StddevSamp,\n  Sum,\n  VariancePop,\n  VarianceSamp\n}\n\ncase class VersionSpecificAggregateExpressionExtractor(expressionExtractor: ExpressionExtractor,\n                                                       context: SQLGenContext,\n                                                       filter: Option[SQLGen.Joinable]) {\n  def unapply(aggFunc: AggregateFunction): Option[Statement] = {\n    aggFunc match {\n      // CentralMomentAgg.scala\n      case StddevPop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_POP\", child, filter))\n      case StddevSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_SAMP\", child, filter))\n      case VariancePop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_POP\", child, filter))\n      case VarianceSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_SAMP\", child, filter))\n      case Kurtosis(expressionExtractor(child), true) =>\n        // ( (AVG(POW(child, 4)) - AVG(child) * POW(AVG(child), 3) * 4  + 6 * AVG(POW(child), 2) * POW(AVG(child), 2)  - 3 * POW(AVG(child), 4) )\n        //  / POW(STD(child), 4) )  - 3\n        // following the formula from https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ article\n        Some(\n          op(\n            \"-\",\n            op(\n              \"/\",\n              op(\n                \"-\",\n                op(\n                  \"+\",\n                  op(\n                    \"-\",\n                    aggregateWithFilter(\"AVG\", f(\"POW\", child, \"4\"), filter),\n                    op(\"*\",\n                      op(\"*\",\n                        aggregateWithFilter(\"AVG\", child, filter),\n                        aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter)),\n                      \"4\")\n                  ),\n                  op(\"*\",\n                    \"6\",\n                    op(\"*\",\n                      aggregateWithFilter(\"AVG\", f(\"POW\", child, \"2\"), filter),\n                      f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"2\")))\n                ),\n                op(\"*\", \"3\", f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"4\"))\n              ),\n              f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"4\")\n            ),\n            \"3\"\n          )\n        )\n\n      case Skewness(expressionExtractor(child), true) =>\n        // (AVG(POW(child, 3)) - AVG(child) * POW(STD(child), 2) * 3 - POW(AVG(child), 3) ) / POW(STD(child), 3)\n        // following the definition section in https://en.wikipedia.org/wiki/Skewness\n        Some(\n          op(\n            \"/\",\n            op(\n              \"-\",\n              op(\n                \"-\",\n                aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter),\n                op(\"*\",\n                  op(\"*\",\n                    aggregateWithFilter(\"AVG\", child, filter),\n                    f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"2\")),\n                  \"3\")\n              ),\n              f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"3\")\n            ),\n            f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"3\")\n          )\n        )\n\n      // First.scala\n      case First(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Last.scala\n      case Last(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Sum.scala\n      case Sum(expressionExtractor(child)) =>\n        Some(aggregateWithFilter(\"SUM\", child, filter))\n\n      // Average.scala\n      case Average(expressionExtractor(child)) =>\n        Some(aggregateWithFilter(\"AVG\", child, filter))\n\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/VersionSpecificAggregateExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression}\nimport org.apache.spark.sql.catalyst.plans.logical.{Aggregate, LogicalPlan}\n\nobject VersionSpecificAggregateExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[Expression], Seq[NamedExpression], LogicalPlan)] =\n    plan match {\n      case a @ Aggregate(groupingExpressions, aggregateExpressions, child) =>\n        Option(groupingExpressions, aggregateExpressions, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/VersionSpecificExpressionGen.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.ExpressionGen._\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, IntVar, Joinable, Raw, StringVar, block, cast, sqlMapValueCaseInsensitive, stringToJoinable}\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.types.{BinaryType, BooleanType, ByteType, DateType, DecimalType, DoubleType, FloatType, IntegerType, LongType, NullType, ShortType, StringType, TimestampType}\n\ncase class VersionSpecificExpressionGen(expressionExtractor: ExpressionExtractor) {\n  def unapply(e: Expression): Option[Joinable] = e match {\n    case MakeDate(expressionExtractor(year),\n    expressionExtractor(month),\n    expressionExtractor(day),\n    false) =>\n      Some(f(\"DATE\", f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day)))\n    case MakeTimestamp(expressionExtractor(year),\n    expressionExtractor(month),\n    expressionExtractor(day),\n    expressionExtractor(hour),\n    expressionExtractor(min),\n    expressionExtractor(sec),\n    _,\n    _,\n    false) =>\n      Some(\n        f(\"TIMESTAMP\",\n          f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day, \"' '\", hour, \"':'\", min, \"':'\", sec)))\n\n    case Elt(expressionExtractor(Some(child)), false) => Some(f(\"ELT\", child))\n\n    case IntegralDivide(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(f(\"FLOOR\", op(\"/\", left, right)))\n\n    // arithmetic.scala\n    case Add(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"+\", left, right))\n    case Subtract(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"-\", left, right))\n    case Multiply(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"*\", left, right))\n    case Divide(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"/\", left, right))\n    case Remainder(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"%\", left, right))\n    case Abs(expressionExtractor(child)) => Some(f(\"ABS\", child))\n\n    case Pmod(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(block(block(block(left + \"%\" + right) + \"+\" + right) + \"%\" + right))\n\n    // SingleStore and spark support other date formats\n    // UnixTime doesn't use format if time is already a dataType or TimestampType\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n      if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n      if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    // regexpExpression.scala\n    case RegExpReplace(expressionExtractor(subject),\n    expressionExtractor(regexp),\n    expressionExtractor(rep),\n    pos) if pos.foldable && pos.eval() == null =>\n      Some(f(\"REGEXP_REPLACE\", subject, regexp, rep, StringVar(\"g\")))\n\n    case RegExpReplace(expressionExtractor(subject),\n    expressionExtractor(regexp),\n    expressionExtractor(rep),\n    intFoldableExtractor(pos)) =>\n      Some(\n        f(\"CONCAT\",\n          f(\"LEFT\", subject, IntVar(pos - 1)),\n          f(\"REGEXP_REPLACE\",\n            f(\"RIGHT\", subject, op(\"-\", f(\"LENGTH\", subject), IntVar(pos - 1))),\n            regexp,\n            rep,\n            StringVar(\"g\"))))\n\n    case UnaryMinus(expressionExtractor(child), false) => Some(f(\"-\", child))\n\n    // randomExpression.scala\n    // TODO PLAT-5759\n    case Rand(expressionExtractor(child), false) => Some(f(\"RAND\", child))\n\n    // Cast.scala\n    case Cast(e @ expressionExtractor(child), dataType, _) => {\n      dataType match {\n        case TimestampType => {\n          e.dataType match {\n            case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n                 DoubleType | _: DecimalType =>\n              Some(cast(f(\"FROM_UNIXTIME\", child), \"DATETIME(6)\"))\n            case _ => Some(cast(child, \"DATETIME(6)\"))\n          }\n        }\n        case DateType => Some(cast(child, \"DATE\"))\n\n        case StringType => Some(cast(child, \"CHAR\"))\n        case BinaryType => Some(cast(child, \"BINARY\"))\n\n        case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n             DoubleType | _: DecimalType =>\n          if (e.dataType == DateType) {\n            Some(StringVar(null))\n          } else {\n            val numeric_ch = if (e.dataType == TimestampType) {\n              f(\"UNIX_TIMESTAMP\", child)\n            } else {\n              child\n            }\n            dataType match {\n              case BooleanType     => Some(op(\"!=\", numeric_ch, IntVar(0)))\n              case ByteType        => Some(op(\"!:>\", numeric_ch, \"TINYINT\"))\n              case ShortType       => Some(op(\"!:>\", numeric_ch, \"SMALLINT\"))\n              case IntegerType     => Some(op(\"!:>\", numeric_ch, \"INT\"))\n              case LongType        => Some(op(\"!:>\", numeric_ch, \"BIGINT\"))\n              case FloatType       => Some(op(\"!:>\", numeric_ch, \"FLOAT\"))\n              case DoubleType      => Some(op(\"!:>\", numeric_ch, \"DOUBLE\"))\n              case dt: DecimalType => Some(makeDecimal(numeric_ch, dt.precision, dt.scale))\n            }\n          }\n        // SingleStore doesn't know how to handle this cast, pass it through AS is\n        case _ => Some(child)\n      }\n    }\n\n    case NextDay(expressionExtractor(startDate), utf8StringFoldableExtractor(dayOfWeek)) =>\n      Some(\n        computeNextDay(\n          startDate,\n          sqlMapValueCaseInsensitive(\n            StringVar(dayOfWeek.toString),\n            DAYS_OF_WEEK_OFFSET_MAP,\n            StringVar(null)\n          )\n        ))\n\n    case DateFromUnixDate(expressionExtractor(child)) => Some(f(\"FROM_UNIXTIME\", child))\n    case UnixDate(expressionExtractor(child)) => Some(f(\"TIMESTAMPDIFF\", \"DAY\", \"'1970-01-01'\",  child))\n    case UnixSeconds(expressionExtractor(child)) => Some(f(\"TIMESTAMPDIFF\", \"SECOND\", \"'1970-01-01 00:00:00'\",  child))\n    case UnixMicros(expressionExtractor(child)) => Some(f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\",  child))\n    case UnixMillis(expressionExtractor(child)) => Some(f(\"ROUND\", op(\"/\",  f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\",  child), \"1000\")))\n    case SecondsToTimestamp(expressionExtractor(child)) => Some(f(\"TIMESTAMPADD\", \"SECOND\", child, \"'1970-01-01 00:00:00'\"))\n    case MillisToTimestamp(expressionExtractor(child)) => Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", op(\"*\", child, \"1000\"),  \"'1970-01-01 00:00:00'\"))\n    case MicrosToTimestamp(expressionExtractor(child)) => Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", child,  \"'1970-01-01 00:00:00'\"))\n\n    case LengthOfJsonArray(expressionExtractor(child)) => Some(f(\"LENGTH\", f(\"JSON_TO_ARRAY\", child)))\n\n    case NextDay(expressionExtractor(startDate), expressionExtractor(dayOfWeek)) =>\n      Some(\n        computeNextDay(startDate,\n          sqlMapValueCaseInsensitive(\n            dayOfWeek,\n            DAYS_OF_WEEK_OFFSET_MAP,\n            StringVar(null)\n          )))\n\n    case Lead(expressionExtractor(input), expressionExtractor(offset), Literal(null, NullType)) =>\n      Some(f(\"LEAD\", input, offset))\n    case Lag(expressionExtractor(input), expressionExtractor(offset), Literal(null, NullType)) =>\n      Some(f(\"LAG\", input, offset))\n\n    case LikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"OR\"))\n    }\n    case NotLikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"AND\")))\n    }\n    case LikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"AND\"))\n    }\n    case NotLikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"OR\")))\n    }\n\n    case WidthBucket(expressionExtractor(value), expressionExtractor(minValue), expressionExtractor(maxValue), expressionExtractor(numBucket)) => {\n      var caseBranches = stringToJoinable(\"\")\n      // when (numBucket <= 0) or (minValue = maxValue)  then null\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"<=\", numBucket, IntVar(0)),\n        op(\"=\", minValue, maxValue),\n      ) + Raw(\"THEN \") + StringVar(null)\n\n      // when (value < minValue and minValue < maxValue) or (value > minValue and minValue > maxValue) then 0\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\"<\", value, minValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\">\", value, minValue), op(\">\", minValue, maxValue))\n      ) + Raw(\"THEN 0\")\n\n      // when (value > maxValue and minValue < maxValue) or (value < maxValue and minValue > maxValue) then numBucket + 1\n      caseBranches += Raw(\"WHEN\") + op(\"|\",\n        op(\"&\", op(\">\", value, maxValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\"<\", value, maxValue), op(\">\", minValue, maxValue))) +\n        Raw(\"THEN\") + op(\"+\", numBucket, \"1\")\n\n      // else FLOOR( (value - minValue)*numBucket / (maxValue - minValue) ) + 1 END\n      val elseBranch = Raw(\"ELSE\") + op(\n        \"+\",\n        f(\"FLOOR\",\n          op(\"/\", op(\"*\", numBucket, op(\"-\", value, minValue)), op(\"-\", maxValue, minValue))),\n        IntVar(1)\n      )\n      Some(block(Raw(\"CASE\") + caseBranches + elseBranch + Raw(\"END\")))\n    }\n\n    // nullExpressions.scala\n    case IfNull(expressionExtractor(left), expressionExtractor(right), _) =>\n      Some(f(\"COALESCE\", left, right))\n\n    // stringExpressions.scala\n    case Left(expressionExtractor(str), expressionExtractor(len), expressionExtractor(child)) =>\n      Some(f(\"LEFT\", str, len, child))\n    case Right(expressionExtractor(str), expressionExtractor(len), expressionExtractor(child)) =>\n      Some(f(\"RIGHT\", str, len, child))\n\n    case Base64(expressionExtractor(child))   => Some(f(\"TO_BASE64\", child))\n    case UnBase64(expressionExtractor(child)) => Some(f(\"FROM_BASE64\", child))\n\n    case Round(expressionExtractor(child), expressionExtractor(scale))    => Some(f(\"ROUND\", child, scale))\n    case Unhex(expressionExtractor(child))     => Some(f(\"UNHEX\", child))\n\n    // ----------------------------------\n    // Ternary Expressions\n    // ----------------------------------\n\n    // mathExpressions.scala\n    case Conv(expressionExtractor(numExpr),\n    intFoldableExtractor(fromBase),\n    intFoldableExtractor(toBase))\n      // SingleStore supports bases only from [2, 36]\n      if fromBase >= 2 && fromBase <= 36 &&\n        toBase >= 2 && toBase <= 36 =>\n      Some(f(\"CONV\", numExpr, IntVar(fromBase), IntVar(toBase)))\n\n    case _ => None\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/VersionSpecificSortExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Sort}\nimport org.apache.spark.sql.execution.datasources.LogicalRelation\n\nobject VersionSpecificSortExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[SortOrder], Boolean, LogicalPlan)] =\n    plan match {\n      case s @ Sort(order, global, child) =>\n        Option(order, global, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/VersionSpecificUtil.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.types.{CalendarIntervalType, DataType}\n\nobject VersionSpecificUtil {\n  def isIntervalType(d:DataType): Boolean =\n    d.isInstanceOf[CalendarIntervalType]\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/VersionSpecificWindowBoundaryExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport org.apache.spark.sql.catalyst.expressions.{Expression, UnaryMinus}\n\ncase class VersionSpecificWindowBoundaryExpressionExtractor(\n                                                             expressionExtractor: ExpressionExtractor) {\n  def unapply(arg: Expression): Option[Statement] = {\n    arg match {\n      case UnaryMinus(expressionExtractor(child), false) =>\n        Some(child + \"PRECEDING\")\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.1/spark/VersionSpecificWindowExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Window}\n\nobject VersionSpecificWindowExtractor {\n  def unapply(plan: LogicalPlan)\n    : Option[(Seq[NamedExpression], Seq[Expression], Seq[SortOrder], LogicalPlan)] =\n    plan match {\n      case Window(windowExpressions, partitionSpec, orderSpec, child) =>\n        Option(windowExpressions, partitionSpec, orderSpec, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/MaxNumConcurrentTasks.scala",
    "content": "package org.apache.spark.scheduler\n\nimport org.apache.spark.rdd.RDD\n\nobject MaxNumConcurrentTasks {\n  def get(rdd: RDD[_]): Int = {\n    val (_, resourceProfiles) =\n      rdd.sparkContext.dagScheduler.getShuffleDependenciesAndResourceProfiles(rdd)\n    val resourceProfile =\n      rdd.sparkContext.dagScheduler.mergeResourceProfilesForStage(resourceProfiles)\n    rdd.sparkContext.maxNumConcurrentTasks(resourceProfile)\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/VersionSpecificAggregateExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport com.singlestore.spark.ExpressionGen.{aggregateWithFilter, f, op}\nimport org.apache.spark.sql.catalyst.expressions.aggregate.{\n  AggregateFunction,\n  Average,\n  First,\n  Kurtosis,\n  Last,\n  Skewness,\n  StddevPop,\n  StddevSamp,\n  Sum,\n  VariancePop,\n  VarianceSamp\n}\n\ncase class VersionSpecificAggregateExpressionExtractor(expressionExtractor: ExpressionExtractor,\n                                                       context: SQLGenContext,\n                                                       filter: Option[SQLGen.Joinable]) {\n  def unapply(aggFunc: AggregateFunction): Option[Statement] = {\n    aggFunc match {\n      // CentralMomentAgg.scala\n      case StddevPop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_POP\", child, filter))\n      case StddevSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_SAMP\", child, filter))\n      case VariancePop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_POP\", child, filter))\n      case VarianceSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_SAMP\", child, filter))\n      case Kurtosis(expressionExtractor(child), true) =>\n        // ( (AVG(POW(child, 4)) - AVG(child) * POW(AVG(child), 3) * 4  + 6 * AVG(POW(child), 2) * POW(AVG(child), 2)  - 3 * POW(AVG(child), 4) )\n        //  / POW(STD(child), 4) )  - 3\n        // following the formula from https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ article\n        Some(\n          op(\n            \"-\",\n            op(\n              \"/\",\n              op(\n                \"-\",\n                op(\n                  \"+\",\n                  op(\n                    \"-\",\n                    aggregateWithFilter(\"AVG\", f(\"POW\", child, \"4\"), filter),\n                    op(\"*\",\n                       op(\"*\",\n                          aggregateWithFilter(\"AVG\", child, filter),\n                          aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter)),\n                       \"4\")\n                  ),\n                  op(\"*\",\n                     \"6\",\n                     op(\"*\",\n                        aggregateWithFilter(\"AVG\", f(\"POW\", child, \"2\"), filter),\n                        f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"2\")))\n                ),\n                op(\"*\", \"3\", f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"4\"))\n              ),\n              f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"4\")\n            ),\n            \"3\"\n          )\n        )\n\n      case Skewness(expressionExtractor(child), true) =>\n        // (AVG(POW(child, 3)) - AVG(child) * POW(STD(child), 2) * 3 - POW(AVG(child), 3) ) / POW(STD(child), 3)\n        // following the definition section in https://en.wikipedia.org/wiki/Skewness\n        Some(\n          op(\n            \"/\",\n            op(\n              \"-\",\n              op(\n                \"-\",\n                aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter),\n                op(\"*\",\n                   op(\"*\",\n                      aggregateWithFilter(\"AVG\", child, filter),\n                      f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"2\")),\n                   \"3\")\n              ),\n              f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"3\")\n            ),\n            f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"3\")\n          )\n        )\n\n      // First.scala\n      case First(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Last.scala\n      case Last(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Sum.scala\n      case Sum(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"SUM\", child, filter))\n\n      // Average.scala\n      case Average(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"AVG\", child, filter))\n\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/VersionSpecificAggregateExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression}\nimport org.apache.spark.sql.catalyst.plans.logical.{Aggregate, LogicalPlan}\n\nobject VersionSpecificAggregateExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[Expression], Seq[NamedExpression], LogicalPlan)] =\n    plan match {\n      case a @ Aggregate(groupingExpressions, aggregateExpressions, child) =>\n        Option(groupingExpressions, aggregateExpressions, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/VersionSpecificExpressionGen.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.ExpressionGen._\nimport com.singlestore.spark.SQLGen.{\n  ExpressionExtractor,\n  IntVar,\n  Joinable,\n  Raw,\n  StringVar,\n  block,\n  cast,\n  sqlMapValueCaseInsensitive,\n  stringToJoinable\n}\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.types.{\n  BinaryType,\n  BooleanType,\n  ByteType,\n  DateType,\n  DayTimeIntervalType,\n  DecimalType,\n  DoubleType,\n  FloatType,\n  IntegerType,\n  LongType,\n  NullType,\n  ShortType,\n  StringType,\n  TimestampType,\n  YearMonthIntervalType\n}\n\ncase class VersionSpecificExpressionGen(expressionExtractor: ExpressionExtractor) {\n  def unapply(e: Expression): Option[Joinable] = e match {\n    case MakeDate(expressionExtractor(year),\n                  expressionExtractor(month),\n                  expressionExtractor(day),\n                  false) =>\n      Some(f(\"DATE\", f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day)))\n    case MakeTimestamp(expressionExtractor(year),\n                       expressionExtractor(month),\n                       expressionExtractor(day),\n                       expressionExtractor(hour),\n                       expressionExtractor(min),\n                       expressionExtractor(sec),\n                       _,\n                       _,\n                       false,\n                       TimestampType) =>\n      Some(\n        f(\"TIMESTAMP\",\n          f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day, \"' '\", hour, \"':'\", min, \"':'\", sec)))\n\n    case Elt(expressionExtractor(Some(child)), false) => Some(f(\"ELT\", child))\n\n    case IntegralDivide(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(f(\"FLOOR\", op(\"/\", left, right)))\n\n    // arithmetic.scala\n    case Add(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"+\", left, right))\n    case Subtract(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"-\", left, right))\n    case Multiply(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"*\", left, right))\n    case Divide(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"/\", left, right))\n    case Remainder(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"%\", left, right))\n    case Abs(expressionExtractor(child), false) => Some(f(\"ABS\", child))\n\n    case Pmod(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(block(block(block(left + \"%\" + right) + \"+\" + right) + \"%\" + right))\n\n    // SingleStore and spark support other date formats\n    // UnixTime doesn't use format if time is already a dataType or TimestampType\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    // regexpExpression.scala\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       pos) if pos.foldable && pos.eval() == null =>\n      Some(f(\"REGEXP_REPLACE\", subject, regexp, rep, StringVar(\"g\")))\n\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       intFoldableExtractor(pos)) =>\n      Some(\n        f(\"CONCAT\",\n          f(\"LEFT\", subject, IntVar(pos - 1)),\n          f(\"REGEXP_REPLACE\",\n            f(\"RIGHT\", subject, op(\"-\", f(\"LENGTH\", subject), IntVar(pos - 1))),\n            regexp,\n            rep,\n            StringVar(\"g\"))))\n\n    case UnaryMinus(expressionExtractor(child), false) => Some(f(\"-\", child))\n\n    // randomExpression.scala\n    // TODO PLAT-5759\n    case Rand(expressionExtractor(child), false) => Some(f(\"RAND\", child))\n\n    // Cast.scala\n    case Cast(e @ expressionExtractor(child), dataType, _, false) => {\n      dataType match {\n        case TimestampType => {\n          e.dataType match {\n            case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n                DoubleType | _: DecimalType =>\n              Some(cast(f(\"FROM_UNIXTIME\", child), \"DATETIME(6)\"))\n            case _ => Some(cast(child, \"DATETIME(6)\"))\n          }\n        }\n        case DateType => Some(cast(child, \"DATE\"))\n\n        case StringType => Some(cast(child, \"CHAR\"))\n        case BinaryType => Some(cast(child, \"BINARY\"))\n\n        case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n            DoubleType | _: DecimalType =>\n          if (e.dataType == DateType) {\n            Some(StringVar(null))\n          } else {\n            val numeric_ch = if (e.dataType == TimestampType) {\n              f(\"UNIX_TIMESTAMP\", child)\n            } else {\n              child\n            }\n            dataType match {\n              case BooleanType     => Some(op(\"!=\", numeric_ch, IntVar(0)))\n              case ByteType        => Some(op(\"!:>\", numeric_ch, \"TINYINT\"))\n              case ShortType       => Some(op(\"!:>\", numeric_ch, \"SMALLINT\"))\n              case IntegerType     => Some(op(\"!:>\", numeric_ch, \"INT\"))\n              case LongType        => Some(op(\"!:>\", numeric_ch, \"BIGINT\"))\n              case FloatType       => Some(op(\"!:>\", numeric_ch, \"FLOAT\"))\n              case DoubleType      => Some(op(\"!:>\", numeric_ch, \"DOUBLE\"))\n              case dt: DecimalType => Some(makeDecimal(numeric_ch, dt.precision, dt.scale))\n            }\n          }\n        // SingleStore doesn't know how to handle this cast, pass it through AS is\n        case _ => Some(child)\n      }\n    }\n\n    case DateFromUnixDate(expressionExtractor(child)) => Some(f(\"FROM_UNIXTIME\", child))\n    case UnixDate(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"DAY\", \"'1970-01-01'\", child))\n    case UnixSeconds(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"SECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMicros(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMillis(expressionExtractor(child)) =>\n      Some(\n        f(\"ROUND\",\n          op(\"/\", f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child), \"1000\")))\n    case SecondsToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"SECOND\", child, \"'1970-01-01 00:00:00'\"))\n    case MillisToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", op(\"*\", child, \"1000\"), \"'1970-01-01 00:00:00'\"))\n    case MicrosToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", child, \"'1970-01-01 00:00:00'\"))\n\n    case LengthOfJsonArray(expressionExtractor(child)) =>\n      Some(f(\"LENGTH\", f(\"JSON_TO_ARRAY\", child)))\n\n    case NextDay(expressionExtractor(startDate), utf8StringFoldableExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(\n          startDate,\n          sqlMapValueCaseInsensitive(\n            StringVar(dayOfWeek.toString),\n            DAYS_OF_WEEK_OFFSET_MAP,\n            StringVar(null)\n          )\n        ))\n\n    case NextDay(expressionExtractor(startDate), expressionExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(startDate,\n                       sqlMapValueCaseInsensitive(\n                         dayOfWeek,\n                         DAYS_OF_WEEK_OFFSET_MAP,\n                         StringVar(null)\n                       )))\n\n    case TimeAdd(expressionExtractor(start),\n                 Literal(v: Long, DayTimeIntervalType(_, _)),\n                 timeZoneId) => {\n      Some(addMicroseconds(start, v))\n    }\n\n    case TimestampAddYMInterval(expressionExtractor(start),\n                                Literal(v: Int, YearMonthIntervalType(_, _)),\n                                timeZoneId) => {\n      Some(addMonths(start, v))\n    }\n\n    case Lead(expressionExtractor(input),\n              expressionExtractor(offset),\n              Literal(null, NullType),\n              false) =>\n      Some(f(\"LEAD\", input, offset))\n    case Lag(expressionExtractor(input),\n             expressionExtractor(offset),\n             Literal(null, NullType),\n             false) =>\n      Some(f(\"LAG\", input, offset))\n\n    case BitwiseGet(expressionExtractor(left), expressionExtractor(right)) =>\n      Some(op(\"&\", op(\">>\", left, right), \"1\"))\n\n    case LikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"OR\"))\n    }\n    case NotLikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"AND\")))\n    }\n    case LikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"AND\"))\n    }\n    case NotLikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"OR\")))\n    }\n\n    case WidthBucket(expressionExtractor(value),\n                     expressionExtractor(minValue),\n                     expressionExtractor(maxValue),\n                     expressionExtractor(numBucket)) => {\n      var caseBranches = stringToJoinable(\"\")\n      // when (numBucket <= 0) or (minValue = maxValue)  then null\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"<=\", numBucket, IntVar(0)),\n        op(\"=\", minValue, maxValue),\n      ) + Raw(\"THEN \") + StringVar(null)\n\n      // when (value < minValue and minValue < maxValue) or (value > minValue and minValue > maxValue) then 0\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\"<\", value, minValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\">\", value, minValue), op(\">\", minValue, maxValue))\n      ) + Raw(\"THEN 0\")\n\n      // when (value > maxValue and minValue < maxValue) or (value < maxValue and minValue > maxValue) then numBucket + 1\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\">\", value, maxValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\"<\", value, maxValue), op(\">\", minValue, maxValue))) +\n        Raw(\"THEN\") + op(\"+\", numBucket, \"1\")\n\n      // else FLOOR( (value - minValue)*numBucket / (maxValue - minValue) ) + 1 END\n      val elseBranch = Raw(\"ELSE\") + op(\n        \"+\",\n        f(\"FLOOR\",\n          op(\"/\", op(\"*\", numBucket, op(\"-\", value, minValue)), op(\"-\", maxValue, minValue))),\n        IntVar(1)\n      )\n      Some(block(Raw(\"CASE\") + caseBranches + elseBranch + Raw(\"END\")))\n    }\n\n    // nullExpressions.scala\n    case IfNull(expressionExtractor(left), expressionExtractor(right), _) =>\n      Some(f(\"COALESCE\", left, right))\n\n    // stringExpressions.scala\n    case Left(expressionExtractor(str), expressionExtractor(len), expressionExtractor(child)) =>\n      Some(f(\"LEFT\", str, len, child))\n    case Right(expressionExtractor(str), expressionExtractor(len), expressionExtractor(child)) =>\n      Some(f(\"RIGHT\", str, len, child))\n\n    case Base64(expressionExtractor(child))   => Some(f(\"TO_BASE64\", child))\n    case UnBase64(expressionExtractor(child)) => Some(f(\"FROM_BASE64\", child))\n\n    case Round(expressionExtractor(child), expressionExtractor(scale)) =>\n      Some(f(\"ROUND\", child, scale))\n    case Unhex(expressionExtractor(child)) => Some(f(\"UNHEX\", child))\n\n    // ----------------------------------\n    // Ternary Expressions\n    // ----------------------------------\n\n    // mathExpressions.scala\n    case Conv(expressionExtractor(numExpr),\n              intFoldableExtractor(fromBase),\n              intFoldableExtractor(toBase))\n        // SingleStore supports bases only from [2, 36]\n        if fromBase >= 2 && fromBase <= 36 &&\n          toBase >= 2 && toBase <= 36 =>\n      Some(f(\"CONV\", numExpr, IntVar(fromBase), IntVar(toBase)))\n\n    case _ => None\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/VersionSpecificSortExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Sort}\nimport org.apache.spark.sql.execution.datasources.LogicalRelation\n\nobject VersionSpecificSortExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[SortOrder], Boolean, LogicalPlan)] =\n    plan match {\n      case s @ Sort(order, global, child) =>\n        Option(order, global, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/VersionSpecificUtil.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.types.{\n  CalendarIntervalType,\n  DataType,\n  DayTimeIntervalType,\n  YearMonthIntervalType\n}\n\nobject VersionSpecificUtil {\n  def isIntervalType(d: DataType): Boolean =\n    d.isInstanceOf[CalendarIntervalType] || d.isInstanceOf[DayTimeIntervalType] || d\n      .isInstanceOf[YearMonthIntervalType]\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/VersionSpecificWindowBoundaryExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport org.apache.spark.sql.catalyst.expressions.{Expression, UnaryMinus}\n\ncase class VersionSpecificWindowBoundaryExpressionExtractor(\n    expressionExtractor: ExpressionExtractor) {\n  def unapply(arg: Expression): Option[Statement] = {\n    arg match {\n      case UnaryMinus(expressionExtractor(child), false) =>\n        Some(child + \"PRECEDING\")\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.2/spark/VersionSpecificWindowExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Window}\n\nobject VersionSpecificWindowExtractor {\n  def unapply(plan: LogicalPlan)\n    : Option[(Seq[NamedExpression], Seq[Expression], Seq[SortOrder], LogicalPlan)] =\n    plan match {\n      case Window(windowExpressions, partitionSpec, orderSpec, child) =>\n        Option(windowExpressions, partitionSpec, orderSpec, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/MaxNumConcurrentTasks.scala",
    "content": "package org.apache.spark.scheduler\n\nimport org.apache.spark.rdd.RDD\n\nobject MaxNumConcurrentTasks {\n  def get(rdd: RDD[_]): Int = {\n    val (_, resourceProfiles) =\n      rdd.sparkContext.dagScheduler.getShuffleDependenciesAndResourceProfiles(rdd)\n    val resourceProfile =\n      rdd.sparkContext.dagScheduler.mergeResourceProfilesForStage(resourceProfiles)\n    rdd.sparkContext.maxNumConcurrentTasks(resourceProfile)\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/VersionSpecificAggregateExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport com.singlestore.spark.ExpressionGen.{aggregateWithFilter, f, op}\nimport org.apache.spark.sql.catalyst.expressions.aggregate.{\n  AggregateFunction,\n  Average,\n  First,\n  Kurtosis,\n  Last,\n  Skewness,\n  StddevPop,\n  StddevSamp,\n  Sum,\n  VariancePop,\n  VarianceSamp\n}\n\ncase class VersionSpecificAggregateExpressionExtractor(expressionExtractor: ExpressionExtractor,\n                                                       context: SQLGenContext,\n                                                       filter: Option[SQLGen.Joinable]) {\n  def unapply(aggFunc: AggregateFunction): Option[Statement] = {\n    aggFunc match {\n      // CentralMomentAgg.scala\n      case StddevPop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_POP\", child, filter))\n      case StddevSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_SAMP\", child, filter))\n      case VariancePop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_POP\", child, filter))\n      case VarianceSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_SAMP\", child, filter))\n      case Kurtosis(expressionExtractor(child), true) =>\n        // ( (AVG(POW(child, 4)) - AVG(child) * POW(AVG(child), 3) * 4  + 6 * AVG(POW(child), 2) * POW(AVG(child), 2)  - 3 * POW(AVG(child), 4) )\n        //  / POW(STD(child), 4) )  - 3\n        // following the formula from https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ article\n        Some(\n          op(\n            \"-\",\n            op(\n              \"/\",\n              op(\n                \"-\",\n                op(\n                  \"+\",\n                  op(\n                    \"-\",\n                    aggregateWithFilter(\"AVG\", f(\"POW\", child, \"4\"), filter),\n                    op(\"*\",\n                       op(\"*\",\n                          aggregateWithFilter(\"AVG\", child, filter),\n                          aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter)),\n                       \"4\")\n                  ),\n                  op(\"*\",\n                     \"6\",\n                     op(\"*\",\n                        aggregateWithFilter(\"AVG\", f(\"POW\", child, \"2\"), filter),\n                        f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"2\")))\n                ),\n                op(\"*\", \"3\", f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"4\"))\n              ),\n              f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"4\")\n            ),\n            \"3\"\n          )\n        )\n\n      case Skewness(expressionExtractor(child), true) =>\n        // (AVG(POW(child, 3)) - AVG(child) * POW(STD(child), 2) * 3 - POW(AVG(child), 3) ) / POW(STD(child), 3)\n        // following the definition section in https://en.wikipedia.org/wiki/Skewness\n        Some(\n          op(\n            \"/\",\n            op(\n              \"-\",\n              op(\n                \"-\",\n                aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter),\n                op(\"*\",\n                   op(\"*\",\n                      aggregateWithFilter(\"AVG\", child, filter),\n                      f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"2\")),\n                   \"3\")\n              ),\n              f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"3\")\n            ),\n            f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"3\")\n          )\n        )\n\n      // First.scala\n      case First(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Last.scala\n      case Last(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Sum.scala\n      case Sum(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"SUM\", child, filter))\n\n      // Average.scala\n      case Average(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"AVG\", child, filter))\n\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/VersionSpecificAggregateExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression}\nimport org.apache.spark.sql.catalyst.plans.logical.{Aggregate, LogicalPlan}\n\nobject VersionSpecificAggregateExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[Expression], Seq[NamedExpression], LogicalPlan)] =\n    plan match {\n      case a @ Aggregate(groupingExpressions, aggregateExpressions, child) =>\n        Option(groupingExpressions, aggregateExpressions, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/VersionSpecificExpressionGen.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.ExpressionGen._\nimport com.singlestore.spark.SQLGen.{\n  ExpressionExtractor,\n  IntVar,\n  Joinable,\n  Raw,\n  StringVar,\n  block,\n  cast,\n  sqlMapValueCaseInsensitive,\n  stringToJoinable\n}\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.types.{\n  BinaryType,\n  BooleanType,\n  ByteType,\n  DateType,\n  DayTimeIntervalType,\n  DecimalType,\n  DoubleType,\n  FloatType,\n  IntegerType,\n  LongType,\n  NullType,\n  ShortType,\n  StringType,\n  TimestampType,\n  YearMonthIntervalType\n}\n\ncase class VersionSpecificExpressionGen(expressionExtractor: ExpressionExtractor) {\n  def unapply(e: Expression): Option[Joinable] = e match {\n    case MakeDate(expressionExtractor(year),\n                  expressionExtractor(month),\n                  expressionExtractor(day),\n                  false) =>\n      Some(f(\"DATE\", f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day)))\n    case MakeTimestamp(expressionExtractor(year),\n                       expressionExtractor(month),\n                       expressionExtractor(day),\n                       expressionExtractor(hour),\n                       expressionExtractor(min),\n                       expressionExtractor(sec),\n                       _,\n                       _,\n                       false,\n                       TimestampType) =>\n      Some(\n        f(\"TIMESTAMP\",\n          f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day, \"' '\", hour, \"':'\", min, \"':'\", sec)))\n\n    case Elt(expressionExtractor(Some(child)), false) => Some(f(\"ELT\", child))\n\n    case IntegralDivide(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(f(\"FLOOR\", op(\"/\", left, right)))\n\n    // arithmetic.scala\n    case Add(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"+\", left, right))\n    case Subtract(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"-\", left, right))\n    case Multiply(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"*\", left, right))\n    case Divide(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"/\", left, right))\n    case Remainder(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(op(\"%\", left, right))\n    case Abs(expressionExtractor(child), false) => Some(f(\"ABS\", child))\n\n    case Pmod(expressionExtractor(left), expressionExtractor(right), false) =>\n      Some(block(block(block(left + \"%\" + right) + \"+\" + right) + \"%\" + right))\n\n    // SingleStore and spark support other date formats\n    // UnixTime doesn't use format if time is already a dataType or TimestampType\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    // regexpExpression.scala\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       pos) if pos.foldable && pos.eval() == null =>\n      Some(f(\"REGEXP_REPLACE\", subject, regexp, rep, StringVar(\"g\")))\n\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       intFoldableExtractor(pos)) =>\n      Some(\n        f(\"CONCAT\",\n          f(\"LEFT\", subject, IntVar(pos - 1)),\n          f(\"REGEXP_REPLACE\",\n            f(\"RIGHT\", subject, op(\"-\", f(\"LENGTH\", subject), IntVar(pos - 1))),\n            regexp,\n            rep,\n            StringVar(\"g\"))))\n\n    case UnaryMinus(expressionExtractor(child), false) => Some(f(\"-\", child))\n\n    // randomExpression.scala\n    // TODO PLAT-5759\n    case Rand(expressionExtractor(child), false) => Some(f(\"RAND\", child))\n\n    // Cast.scala\n    case Cast(e @ expressionExtractor(child), dataType, _, false) => {\n      dataType match {\n        case TimestampType => {\n          e.dataType match {\n            case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n                DoubleType | _: DecimalType =>\n              Some(cast(f(\"FROM_UNIXTIME\", child), \"DATETIME(6)\"))\n            case _ => Some(cast(child, \"DATETIME(6)\"))\n          }\n        }\n        case DateType => Some(cast(child, \"DATE\"))\n\n        case StringType => Some(cast(child, \"CHAR\"))\n        case BinaryType => Some(cast(child, \"BINARY\"))\n\n        case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n            DoubleType | _: DecimalType =>\n          if (e.dataType == DateType) {\n            Some(StringVar(null))\n          } else {\n            val numeric_ch = if (e.dataType == TimestampType) {\n              f(\"UNIX_TIMESTAMP\", child)\n            } else {\n              child\n            }\n            dataType match {\n              case BooleanType     => Some(op(\"!=\", numeric_ch, IntVar(0)))\n              case ByteType        => Some(op(\"!:>\", numeric_ch, \"TINYINT\"))\n              case ShortType       => Some(op(\"!:>\", numeric_ch, \"SMALLINT\"))\n              case IntegerType     => Some(op(\"!:>\", numeric_ch, \"INT\"))\n              case LongType        => Some(op(\"!:>\", numeric_ch, \"BIGINT\"))\n              case FloatType       => Some(op(\"!:>\", numeric_ch, \"FLOAT\"))\n              case DoubleType      => Some(op(\"!:>\", numeric_ch, \"DOUBLE\"))\n              case dt: DecimalType => Some(makeDecimal(numeric_ch, dt.precision, dt.scale))\n            }\n          }\n        // SingleStore doesn't know how to handle this cast, pass it through AS is\n        case _ => Some(child)\n      }\n    }\n\n    case DateFromUnixDate(expressionExtractor(child)) => Some(f(\"FROM_UNIXTIME\", child))\n    case UnixDate(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"DAY\", \"'1970-01-01'\", child))\n    case UnixSeconds(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"SECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMicros(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMillis(expressionExtractor(child)) =>\n      Some(\n        f(\"ROUND\",\n          op(\"/\", f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child), \"1000\")))\n    case SecondsToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"SECOND\", child, \"'1970-01-01 00:00:00'\"))\n    case MillisToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", op(\"*\", child, \"1000\"), \"'1970-01-01 00:00:00'\"))\n    case MicrosToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", child, \"'1970-01-01 00:00:00'\"))\n\n    case LengthOfJsonArray(expressionExtractor(child)) =>\n      Some(f(\"LENGTH\", f(\"JSON_TO_ARRAY\", child)))\n\n    case NextDay(expressionExtractor(startDate), utf8StringFoldableExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(\n          startDate,\n          sqlMapValueCaseInsensitive(\n            StringVar(dayOfWeek.toString),\n            DAYS_OF_WEEK_OFFSET_MAP,\n            StringVar(null)\n          )\n        ))\n\n    case NextDay(expressionExtractor(startDate), expressionExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(startDate,\n                       sqlMapValueCaseInsensitive(\n                         dayOfWeek,\n                         DAYS_OF_WEEK_OFFSET_MAP,\n                         StringVar(null)\n                       )))\n\n    case TimeAdd(expressionExtractor(start),\n                 Literal(v: Long, DayTimeIntervalType(_, _)),\n                 timeZoneId) => {\n      Some(addMicroseconds(start, v))\n    }\n\n    case TimestampAddYMInterval(expressionExtractor(start),\n                                Literal(v: Int, YearMonthIntervalType(_, _)),\n                                timeZoneId) => {\n      Some(addMonths(start, v))\n    }\n\n    case Lead(expressionExtractor(input),\n              expressionExtractor(offset),\n              Literal(null, NullType),\n              false) =>\n      Some(f(\"LEAD\", input, offset))\n    case Lag(expressionExtractor(input),\n             expressionExtractor(offset),\n             Literal(null, NullType),\n             false) =>\n      Some(f(\"LAG\", input, offset))\n\n    case BitwiseGet(expressionExtractor(left), expressionExtractor(right)) =>\n      Some(op(\"&\", op(\">>\", left, right), \"1\"))\n\n    case LikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"OR\"))\n    }\n    case NotLikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"AND\")))\n    }\n    case LikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"AND\"))\n    }\n    case NotLikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"OR\")))\n    }\n\n    case WidthBucket(expressionExtractor(value),\n                     expressionExtractor(minValue),\n                     expressionExtractor(maxValue),\n                     expressionExtractor(numBucket)) => {\n      var caseBranches = stringToJoinable(\"\")\n      // when (numBucket <= 0) or (minValue = maxValue)  then null\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"<=\", numBucket, IntVar(0)),\n        op(\"=\", minValue, maxValue),\n      ) + Raw(\"THEN \") + StringVar(null)\n\n      // when (value < minValue and minValue < maxValue) or (value > minValue and minValue > maxValue) then 0\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\"<\", value, minValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\">\", value, minValue), op(\">\", minValue, maxValue))\n      ) + Raw(\"THEN 0\")\n\n      // when (value > maxValue and minValue < maxValue) or (value < maxValue and minValue > maxValue) then numBucket + 1\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\">\", value, maxValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\"<\", value, maxValue), op(\">\", minValue, maxValue))) +\n        Raw(\"THEN\") + op(\"+\", numBucket, \"1\")\n\n      // else FLOOR( (value - minValue)*numBucket / (maxValue - minValue) ) + 1 END\n      val elseBranch = Raw(\"ELSE\") + op(\n        \"+\",\n        f(\"FLOOR\",\n          op(\"/\", op(\"*\", numBucket, op(\"-\", value, minValue)), op(\"-\", maxValue, minValue))),\n        IntVar(1)\n      )\n      Some(block(Raw(\"CASE\") + caseBranches + elseBranch + Raw(\"END\")))\n    }\n\n    // stringExpressions.scala\n    case Left(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"LEFT\", str, len))\n    case Right(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"RIGHT\", str, len))\n\n    case Round(expressionExtractor(child), expressionExtractor(scale))    => Some(f(\"ROUND\", child, scale))\n    case Unhex(expressionExtractor(child))     => Some(f(\"UNHEX\", child))\n\n    // ----------------------------------\n    // Ternary Expressions\n    // ----------------------------------\n\n    // mathExpressions.scala\n    case Conv(expressionExtractor(numExpr),\n              intFoldableExtractor(fromBase),\n              intFoldableExtractor(toBase))\n        // SingleStore supports bases only from [2, 36]\n        if fromBase >= 2 && fromBase <= 36 &&\n          toBase >= 2 && toBase <= 36 =>\n      Some(f(\"CONV\", numExpr, IntVar(fromBase), IntVar(toBase)))\n\n    case _ => None\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/VersionSpecificSortExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Sort}\nimport org.apache.spark.sql.execution.datasources.LogicalRelation\n\nobject VersionSpecificSortExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[SortOrder], Boolean, LogicalPlan)] =\n    plan match {\n      case s @ Sort(order, global, child) =>\n        Option(order, global, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/VersionSpecificUtil.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.types.{\n  CalendarIntervalType,\n  DataType,\n  DayTimeIntervalType,\n  YearMonthIntervalType\n}\n\nobject VersionSpecificUtil {\n  def isIntervalType(d: DataType): Boolean =\n    d.isInstanceOf[CalendarIntervalType] || d.isInstanceOf[DayTimeIntervalType] || d\n      .isInstanceOf[YearMonthIntervalType]\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/VersionSpecificWindowBoundaryExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport org.apache.spark.sql.catalyst.expressions.{Expression, UnaryMinus}\n\ncase class VersionSpecificWindowBoundaryExpressionExtractor(\n    expressionExtractor: ExpressionExtractor) {\n  def unapply(arg: Expression): Option[Statement] = {\n    arg match {\n      case UnaryMinus(expressionExtractor(child), false) =>\n        Some(child + \"PRECEDING\")\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.3/spark/VersionSpecificWindowExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Window}\n\nobject VersionSpecificWindowExtractor {\n  def unapply(plan: LogicalPlan)\n    : Option[(Seq[NamedExpression], Seq[Expression], Seq[SortOrder], LogicalPlan)] =\n    plan match {\n      case Window(windowExpressions, partitionSpec, orderSpec, child) =>\n        Option(windowExpressions, partitionSpec, orderSpec, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/MaxNumConcurrentTasks.scala",
    "content": "package org.apache.spark.scheduler\n\nimport org.apache.spark.rdd.RDD\n\nobject MaxNumConcurrentTasks {\n  def get(rdd: RDD[_]): Int = {\n    val (_, resourceProfiles) =\n      rdd.sparkContext.dagScheduler.getShuffleDependenciesAndResourceProfiles(rdd)\n    val resourceProfile =\n      rdd.sparkContext.dagScheduler.mergeResourceProfilesForStage(resourceProfiles)\n    rdd.sparkContext.maxNumConcurrentTasks(resourceProfile)\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/VersionSpecificAggregateExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport com.singlestore.spark.ExpressionGen.{aggregateWithFilter, f, op}\nimport org.apache.spark.sql.catalyst.expressions.aggregate.{\n  AggregateFunction,\n  Average,\n  First,\n  Kurtosis,\n  Last,\n  Skewness,\n  StddevPop,\n  StddevSamp,\n  Sum,\n  VariancePop,\n  VarianceSamp\n}\nimport org.apache.spark.sql.catalyst.expressions.EvalMode\n\ncase class VersionSpecificAggregateExpressionExtractor(expressionExtractor: ExpressionExtractor,\n                                                       context: SQLGenContext,\n                                                       filter: Option[SQLGen.Joinable]) {\n  def unapply(aggFunc: AggregateFunction): Option[Statement] = {\n    aggFunc match {\n      // CentralMomentAgg.scala\n      case StddevPop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_POP\", child, filter))\n      case StddevSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_SAMP\", child, filter))\n      case VariancePop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_POP\", child, filter))\n      case VarianceSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_SAMP\", child, filter))\n      case Kurtosis(expressionExtractor(child), true) =>\n        // ( (AVG(POW(child, 4)) - AVG(child) * POW(AVG(child), 3) * 4  + 6 * AVG(POW(child), 2) * POW(AVG(child), 2)  - 3 * POW(AVG(child), 4) )\n        //  / POW(STD(child), 4) )  - 3\n        // following the formula from https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ article\n        Some(\n          op(\n            \"-\",\n            op(\n              \"/\",\n              op(\n                \"-\",\n                op(\n                  \"+\",\n                  op(\n                    \"-\",\n                    aggregateWithFilter(\"AVG\", f(\"POW\", child, \"4\"), filter),\n                    op(\"*\",\n                       op(\"*\",\n                          aggregateWithFilter(\"AVG\", child, filter),\n                          aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter)),\n                       \"4\")\n                  ),\n                  op(\"*\",\n                     \"6\",\n                     op(\"*\",\n                        aggregateWithFilter(\"AVG\", f(\"POW\", child, \"2\"), filter),\n                        f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"2\")))\n                ),\n                op(\"*\", \"3\", f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"4\"))\n              ),\n              f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"4\")\n            ),\n            \"3\"\n          )\n        )\n\n      case Skewness(expressionExtractor(child), true) =>\n        // (AVG(POW(child, 3)) - AVG(child) * POW(STD(child), 2) * 3 - POW(AVG(child), 3) ) / POW(STD(child), 3)\n        // following the definition section in https://en.wikipedia.org/wiki/Skewness\n        Some(\n          op(\n            \"/\",\n            op(\n              \"-\",\n              op(\n                \"-\",\n                aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter),\n                op(\"*\",\n                   op(\"*\",\n                      aggregateWithFilter(\"AVG\", child, filter),\n                      f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"2\")),\n                   \"3\")\n              ),\n              f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"3\")\n            ),\n            f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"3\")\n          )\n        )\n\n      // First.scala\n      case First(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Last.scala\n      case Last(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Sum.scala\n      case Sum(expressionExtractor(child), EvalMode.LEGACY) =>\n        Some(aggregateWithFilter(\"SUM\", child, filter))\n\n      // Average.scala\n      case Average(expressionExtractor(child), EvalMode.LEGACY) =>\n        Some(aggregateWithFilter(\"AVG\", child, filter))\n\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/VersionSpecificAggregateExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression}\nimport org.apache.spark.sql.catalyst.plans.logical.{Aggregate, LogicalPlan}\n\nobject VersionSpecificAggregateExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[Expression], Seq[NamedExpression], LogicalPlan)] =\n    plan match {\n      case a @ Aggregate(groupingExpressions, aggregateExpressions, child) =>\n        Option(groupingExpressions, aggregateExpressions, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/VersionSpecificExpressionGen.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.ExpressionGen._\nimport com.singlestore.spark.SQLGen.{\n  ExpressionExtractor,\n  IntVar,\n  Joinable,\n  Raw,\n  StringVar,\n  block,\n  cast,\n  sqlMapValueCaseInsensitive,\n  stringToJoinable\n}\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.types.{\n  BinaryType,\n  BooleanType,\n  ByteType,\n  DateType,\n  DayTimeIntervalType,\n  DecimalType,\n  DoubleType,\n  FloatType,\n  IntegerType,\n  LongType,\n  NullType,\n  ShortType,\n  StringType,\n  TimestampType,\n  YearMonthIntervalType\n}\n\ncase class VersionSpecificExpressionGen(expressionExtractor: ExpressionExtractor) {\n  def unapply(e: Expression): Option[Joinable] = e match {\n    case MakeDate(expressionExtractor(year),\n                  expressionExtractor(month),\n                  expressionExtractor(day),\n                  false) =>\n      Some(f(\"DATE\", f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day)))\n    case MakeTimestamp(expressionExtractor(year),\n                       expressionExtractor(month),\n                       expressionExtractor(day),\n                       expressionExtractor(hour),\n                       expressionExtractor(min),\n                       expressionExtractor(sec),\n                       _,\n                       _,\n                       false,\n                       TimestampType) =>\n      Some(\n        f(\"TIMESTAMP\",\n          f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day, \"' '\", hour, \"':'\", min, \"':'\", sec)))\n\n    case Elt(expressionExtractor(Some(child)), false) => Some(f(\"ELT\", child))\n\n    case IntegralDivide(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(f(\"FLOOR\", op(\"/\", left, right)))\n\n    // arithmetic.scala\n    case Add(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"+\", left, right))\n    case Subtract(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"-\", left, right))\n    case Multiply(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"*\", left, right))\n    case Divide(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"/\", left, right))\n    case Remainder(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"%\", left, right))\n    case Abs(expressionExtractor(child), false) => Some(f(\"ABS\", child))\n\n    case Pmod(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(block(block(block(left + \"%\" + right) + \"+\" + right) + \"%\" + right))\n\n    // SingleStore and spark support other date formats\n    // UnixTime doesn't use format if time is already a dataType or TimestampType\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    // regexpExpression.scala\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       pos) if pos.foldable && pos.eval() == null =>\n      Some(f(\"REGEXP_REPLACE\", subject, regexp, rep, StringVar(\"g\")))\n\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       intFoldableExtractor(pos)) =>\n      Some(\n        f(\"CONCAT\",\n          f(\"LEFT\", subject, IntVar(pos - 1)),\n          f(\"REGEXP_REPLACE\",\n            f(\"RIGHT\", subject, op(\"-\", f(\"LENGTH\", subject), IntVar(pos - 1))),\n            regexp,\n            rep,\n            StringVar(\"g\"))))\n\n    case UnaryMinus(expressionExtractor(child), false) => Some(f(\"-\", child))\n\n    // randomExpression.scala\n    // TODO PLAT-5759\n    case Rand(expressionExtractor(child), false) => Some(f(\"RAND\", child))\n\n    // Cast.scala\n    case Cast(e @ expressionExtractor(child), dataType, _, EvalMode.LEGACY) => {\n      dataType match {\n        case TimestampType => {\n          e.dataType match {\n            case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n                DoubleType | _: DecimalType =>\n              Some(cast(f(\"FROM_UNIXTIME\", child), \"DATETIME(6)\"))\n            case _ => Some(cast(child, \"DATETIME(6)\"))\n          }\n        }\n        case DateType => Some(cast(child, \"DATE\"))\n\n        case StringType => Some(cast(child, \"CHAR\"))\n        case BinaryType => Some(cast(child, \"BINARY\"))\n\n        case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n            DoubleType | _: DecimalType =>\n          if (e.dataType == DateType) {\n            Some(StringVar(null))\n          } else {\n            val numeric_ch = if (e.dataType == TimestampType) {\n              f(\"UNIX_TIMESTAMP\", child)\n            } else {\n              child\n            }\n            dataType match {\n              case BooleanType     => Some(op(\"!=\", numeric_ch, IntVar(0)))\n              case ByteType        => Some(op(\"!:>\", numeric_ch, \"TINYINT\"))\n              case ShortType       => Some(op(\"!:>\", numeric_ch, \"SMALLINT\"))\n              case IntegerType     => Some(op(\"!:>\", numeric_ch, \"INT\"))\n              case LongType        => Some(op(\"!:>\", numeric_ch, \"BIGINT\"))\n              case FloatType       => Some(op(\"!:>\", numeric_ch, \"FLOAT\"))\n              case DoubleType      => Some(op(\"!:>\", numeric_ch, \"DOUBLE\"))\n              case dt: DecimalType => Some(makeDecimal(numeric_ch, dt.precision, dt.scale))\n            }\n          }\n        // SingleStore doesn't know how to handle this cast, pass it through AS is\n        case _ => Some(child)\n      }\n    }\n\n    case DateFromUnixDate(expressionExtractor(child)) => Some(f(\"FROM_UNIXTIME\", child))\n    case UnixDate(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"DAY\", \"'1970-01-01'\", child))\n    case UnixSeconds(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"SECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMicros(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMillis(expressionExtractor(child)) =>\n      Some(\n        f(\"ROUND\",\n          op(\"/\", f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child), \"1000\")))\n    case SecondsToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"SECOND\", child, \"'1970-01-01 00:00:00'\"))\n    case MillisToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", op(\"*\", child, \"1000\"), \"'1970-01-01 00:00:00'\"))\n    case MicrosToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", child, \"'1970-01-01 00:00:00'\"))\n\n    case LengthOfJsonArray(expressionExtractor(child)) =>\n      Some(f(\"LENGTH\", f(\"JSON_TO_ARRAY\", child)))\n\n    case NextDay(expressionExtractor(startDate), utf8StringFoldableExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(\n          startDate,\n          sqlMapValueCaseInsensitive(\n            StringVar(dayOfWeek.toString),\n            DAYS_OF_WEEK_OFFSET_MAP,\n            StringVar(null)\n          )\n        ))\n\n    case NextDay(expressionExtractor(startDate), expressionExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(startDate,\n                       sqlMapValueCaseInsensitive(\n                         dayOfWeek,\n                         DAYS_OF_WEEK_OFFSET_MAP,\n                         StringVar(null)\n                       )))\n\n    case TimeAdd(expressionExtractor(start),\n                 Literal(v: Long, DayTimeIntervalType(_, _)),\n                 timeZoneId) => {\n      Some(addMicroseconds(start, v))\n    }\n\n    case TimestampAddYMInterval(expressionExtractor(start),\n                                Literal(v: Int, YearMonthIntervalType(_, _)),\n                                timeZoneId) => {\n      Some(addMonths(start, v))\n    }\n\n    case Lead(expressionExtractor(input),\n              expressionExtractor(offset),\n              Literal(null, NullType),\n              false) =>\n      Some(f(\"LEAD\", input, offset))\n    case Lag(expressionExtractor(input),\n             expressionExtractor(offset),\n             Literal(null, NullType),\n             false) =>\n      Some(f(\"LAG\", input, offset))\n\n    case BitwiseGet(expressionExtractor(left), expressionExtractor(right)) =>\n      Some(op(\"&\", op(\">>\", left, right), \"1\"))\n\n    case LikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"OR\"))\n    }\n    case NotLikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"AND\")))\n    }\n    case LikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"AND\"))\n    }\n    case NotLikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"OR\")))\n    }\n\n    case WidthBucket(expressionExtractor(value),\n                     expressionExtractor(minValue),\n                     expressionExtractor(maxValue),\n                     expressionExtractor(numBucket)) => {\n      var caseBranches = stringToJoinable(\"\")\n      // when (numBucket <= 0) or (minValue = maxValue)  then null\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"<=\", numBucket, IntVar(0)),\n        op(\"=\", minValue, maxValue),\n      ) + Raw(\"THEN \") + StringVar(null)\n\n      // when (value < minValue and minValue < maxValue) or (value > minValue and minValue > maxValue) then 0\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\"<\", value, minValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\">\", value, minValue), op(\">\", minValue, maxValue))\n      ) + Raw(\"THEN 0\")\n\n      // when (value > maxValue and minValue < maxValue) or (value < maxValue and minValue > maxValue) then numBucket + 1\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\">\", value, maxValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\"<\", value, maxValue), op(\">\", minValue, maxValue))) +\n        Raw(\"THEN\") + op(\"+\", numBucket, \"1\")\n\n      // else FLOOR( (value - minValue)*numBucket / (maxValue - minValue) ) + 1 END\n      val elseBranch = Raw(\"ELSE\") + op(\n        \"+\",\n        f(\"FLOOR\",\n          op(\"/\", op(\"*\", numBucket, op(\"-\", value, minValue)), op(\"-\", maxValue, minValue))),\n        IntVar(1)\n      )\n      Some(block(Raw(\"CASE\") + caseBranches + elseBranch + Raw(\"END\")))\n    }\n\n    // stringExpressions.scala\n    case Left(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"LEFT\", str, len))\n    case Right(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"RIGHT\", str, len))\n\n    case Round(expressionExtractor(child), expressionExtractor(scale), false)    => Some(f(\"ROUND\", child, scale))\n    case Unhex(expressionExtractor(child), false)     => Some(f(\"UNHEX\", child))\n\n    // ----------------------------------\n    // Ternary Expressions\n    // ----------------------------------\n\n    // mathExpressions.scala\n    case Conv(expressionExtractor(numExpr),\n              intFoldableExtractor(fromBase),\n              intFoldableExtractor(toBase), false)\n        // SingleStore supports bases only from [2, 36]\n        if fromBase >= 2 && fromBase <= 36 &&\n          toBase >= 2 && toBase <= 36 =>\n      Some(f(\"CONV\", numExpr, IntVar(fromBase), IntVar(toBase)))\n\n\n\n    case _ => None\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/VersionSpecificSortExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Sort}\nimport org.apache.spark.sql.execution.datasources.LogicalRelation\n\nobject VersionSpecificSortExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[SortOrder], Boolean, LogicalPlan)] =\n    plan match {\n      case s @ Sort(order, global, child) =>\n        Option(order, global, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/VersionSpecificUtil.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.types.{\n  CalendarIntervalType,\n  DataType,\n  DayTimeIntervalType,\n  YearMonthIntervalType\n}\n\nobject VersionSpecificUtil {\n  def isIntervalType(d: DataType): Boolean =\n    d.isInstanceOf[CalendarIntervalType] || d.isInstanceOf[DayTimeIntervalType] || d\n      .isInstanceOf[YearMonthIntervalType]\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/VersionSpecificWindowBoundaryExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport org.apache.spark.sql.catalyst.expressions.{Expression, UnaryMinus}\n\ncase class VersionSpecificWindowBoundaryExpressionExtractor(\n    expressionExtractor: ExpressionExtractor) {\n  def unapply(arg: Expression): Option[Statement] = {\n    arg match {\n      case UnaryMinus(expressionExtractor(child), false) =>\n        Some(child + \"PRECEDING\")\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.4/spark/VersionSpecificWindowExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Window}\n\nobject VersionSpecificWindowExtractor {\n  def unapply(plan: LogicalPlan)\n    : Option[(Seq[NamedExpression], Seq[Expression], Seq[SortOrder], LogicalPlan)] =\n    plan match {\n      case Window(windowExpressions, partitionSpec, orderSpec, child) =>\n        Option(windowExpressions, partitionSpec, orderSpec, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/MaxNumConcurrentTasks.scala",
    "content": "package org.apache.spark.scheduler\n\nimport org.apache.spark.rdd.RDD\n\nobject MaxNumConcurrentTasks {\n  def get(rdd: RDD[_]): Int = {\n    val (_, resourceProfiles) =\n      rdd.sparkContext.dagScheduler.getShuffleDependenciesAndResourceProfiles(rdd)\n    val resourceProfile =\n      rdd.sparkContext.dagScheduler.mergeResourceProfilesForStage(resourceProfiles)\n    rdd.sparkContext.maxNumConcurrentTasks(resourceProfile)\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/VersionSpecificAggregateExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport com.singlestore.spark.ExpressionGen.{aggregateWithFilter, f, op}\nimport org.apache.spark.sql.catalyst.expressions.aggregate.{\n  AggregateFunction,\n  Average,\n  First,\n  Kurtosis,\n  Last,\n  Skewness,\n  StddevPop,\n  StddevSamp,\n  Sum,\n  VariancePop,\n  VarianceSamp\n}\nimport org.apache.spark.sql.catalyst.expressions.EvalMode\n\ncase class VersionSpecificAggregateExpressionExtractor(expressionExtractor: ExpressionExtractor,\n                                                       context: SQLGenContext,\n                                                       filter: Option[SQLGen.Joinable]) {\n  def unapply(aggFunc: AggregateFunction): Option[Statement] = {\n    aggFunc match {\n      // CentralMomentAgg.scala\n      case StddevPop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_POP\", child, filter))\n      case StddevSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_SAMP\", child, filter))\n      case VariancePop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_POP\", child, filter))\n      case VarianceSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_SAMP\", child, filter))\n      case Kurtosis(expressionExtractor(child), true) =>\n        // ( (AVG(POW(child, 4)) - AVG(child) * POW(AVG(child), 3) * 4  + 6 * AVG(POW(child), 2) * POW(AVG(child), 2)  - 3 * POW(AVG(child), 4) )\n        //  / POW(STD(child), 4) )  - 3\n        // following the formula from https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ article\n        Some(\n          op(\n            \"-\",\n            op(\n              \"/\",\n              op(\n                \"-\",\n                op(\n                  \"+\",\n                  op(\n                    \"-\",\n                    aggregateWithFilter(\"AVG\", f(\"POW\", child, \"4\"), filter),\n                    op(\"*\",\n                       op(\"*\",\n                          aggregateWithFilter(\"AVG\", child, filter),\n                          aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter)),\n                       \"4\")\n                  ),\n                  op(\"*\",\n                     \"6\",\n                     op(\"*\",\n                        aggregateWithFilter(\"AVG\", f(\"POW\", child, \"2\"), filter),\n                        f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"2\")))\n                ),\n                op(\"*\", \"3\", f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"4\"))\n              ),\n              f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"4\")\n            ),\n            \"3\"\n          )\n        )\n\n      case Skewness(expressionExtractor(child), true) =>\n        // (AVG(POW(child, 3)) - AVG(child) * POW(STD(child), 2) * 3 - POW(AVG(child), 3) ) / POW(STD(child), 3)\n        // following the definition section in https://en.wikipedia.org/wiki/Skewness\n        Some(\n          op(\n            \"/\",\n            op(\n              \"-\",\n              op(\n                \"-\",\n                aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter),\n                op(\"*\",\n                   op(\"*\",\n                      aggregateWithFilter(\"AVG\", child, filter),\n                      f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"2\")),\n                   \"3\")\n              ),\n              f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"3\")\n            ),\n            f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"3\")\n          )\n        )\n\n      // First.scala\n      case First(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Last.scala\n      case Last(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Sum.scala\n      case Sum(expressionExtractor(child), EvalMode.LEGACY) =>\n        Some(aggregateWithFilter(\"SUM\", child, filter))\n\n      // Average.scala\n      case Average(expressionExtractor(child), EvalMode.LEGACY) =>\n        Some(aggregateWithFilter(\"AVG\", child, filter))\n\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/VersionSpecificAggregateExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression}\nimport org.apache.spark.sql.catalyst.plans.logical.{Aggregate, LogicalPlan}\n\nobject VersionSpecificAggregateExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[Expression], Seq[NamedExpression], LogicalPlan)] =\n    plan match {\n      case a @ Aggregate(groupingExpressions, aggregateExpressions, child) =>\n        Option(groupingExpressions, aggregateExpressions, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/VersionSpecificExpressionGen.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.ExpressionGen._\nimport com.singlestore.spark.SQLGen.{\n  ExpressionExtractor,\n  IntVar,\n  Joinable,\n  Raw,\n  StringVar,\n  block,\n  cast,\n  sqlMapValueCaseInsensitive,\n  stringToJoinable\n}\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.types.{\n  BinaryType,\n  BooleanType,\n  ByteType,\n  DateType,\n  DayTimeIntervalType,\n  DecimalType,\n  DoubleType,\n  FloatType,\n  IntegerType,\n  LongType,\n  NullType,\n  ShortType,\n  StringType,\n  TimestampType,\n  YearMonthIntervalType\n}\n\ncase class VersionSpecificExpressionGen(expressionExtractor: ExpressionExtractor) {\n  def unapply(e: Expression): Option[Joinable] = e match {\n    case MakeDate(expressionExtractor(year),\n                  expressionExtractor(month),\n                  expressionExtractor(day),\n                  false) =>\n      Some(f(\"DATE\", f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day)))\n    case MakeTimestamp(expressionExtractor(year),\n                       expressionExtractor(month),\n                       expressionExtractor(day),\n                       expressionExtractor(hour),\n                       expressionExtractor(min),\n                       expressionExtractor(sec),\n                       _,\n                       _,\n                       false,\n                       TimestampType) =>\n      Some(\n        f(\"TIMESTAMP\",\n          f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day, \"' '\", hour, \"':'\", min, \"':'\", sec)))\n\n    case Elt(expressionExtractor(Some(child)), false) => Some(f(\"ELT\", child))\n\n    case IntegralDivide(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(f(\"FLOOR\", op(\"/\", left, right)))\n\n    // arithmetic.scala\n    case Add(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"+\", left, right))\n    case Subtract(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"-\", left, right))\n    case Multiply(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"*\", left, right))\n    case Divide(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"/\", left, right))\n    case Remainder(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"%\", left, right))\n    case Abs(expressionExtractor(child), false) => Some(f(\"ABS\", child))\n\n    case Pmod(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(block(block(block(left + \"%\" + right) + \"+\" + right) + \"%\" + right))\n\n    // SingleStore and spark support other date formats\n    // UnixTime doesn't use format if time is already a dataType or TimestampType\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    // regexpExpression.scala\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       pos) if pos.foldable && pos.eval() == null =>\n      Some(f(\"REGEXP_REPLACE\", subject, regexp, rep, StringVar(\"g\")))\n\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       intFoldableExtractor(pos)) =>\n      Some(\n        f(\"CONCAT\",\n          f(\"LEFT\", subject, IntVar(pos - 1)),\n          f(\"REGEXP_REPLACE\",\n            f(\"RIGHT\", subject, op(\"-\", f(\"LENGTH\", subject), IntVar(pos - 1))),\n            regexp,\n            rep,\n            StringVar(\"g\"))))\n\n    case UnaryMinus(expressionExtractor(child), false) => Some(f(\"-\", child))\n\n    // randomExpression.scala\n    // TODO PLAT-5759\n    case Rand(expressionExtractor(child), false) => Some(f(\"RAND\", child))\n\n    // Cast.scala\n    case Cast(e @ expressionExtractor(child), dataType, _, EvalMode.LEGACY) => {\n      dataType match {\n        case TimestampType => {\n          e.dataType match {\n            case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n                DoubleType | _: DecimalType =>\n              Some(cast(f(\"FROM_UNIXTIME\", child), \"DATETIME(6)\"))\n            case _ => Some(cast(child, \"DATETIME(6)\"))\n          }\n        }\n        case DateType => Some(cast(child, \"DATE\"))\n\n        case StringType => Some(cast(child, \"CHAR\"))\n        case BinaryType => Some(cast(child, \"BINARY\"))\n\n        case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n            DoubleType | _: DecimalType =>\n          if (e.dataType == DateType) {\n            Some(StringVar(null))\n          } else {\n            val numeric_ch = if (e.dataType == TimestampType) {\n              f(\"UNIX_TIMESTAMP\", child)\n            } else {\n              child\n            }\n            dataType match {\n              case BooleanType     => Some(op(\"!=\", numeric_ch, IntVar(0)))\n              case ByteType        => Some(op(\"!:>\", numeric_ch, \"TINYINT\"))\n              case ShortType       => Some(op(\"!:>\", numeric_ch, \"SMALLINT\"))\n              case IntegerType     => Some(op(\"!:>\", numeric_ch, \"INT\"))\n              case LongType        => Some(op(\"!:>\", numeric_ch, \"BIGINT\"))\n              case FloatType       => Some(op(\"!:>\", numeric_ch, \"FLOAT\"))\n              case DoubleType      => Some(op(\"!:>\", numeric_ch, \"DOUBLE\"))\n              case dt: DecimalType => Some(makeDecimal(numeric_ch, dt.precision, dt.scale))\n            }\n          }\n        // SingleStore doesn't know how to handle this cast, pass it through AS is\n        case _ => Some(child)\n      }\n    }\n\n    case DateFromUnixDate(expressionExtractor(child)) => Some(f(\"FROM_UNIXTIME\", child))\n    case UnixDate(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"DAY\", \"'1970-01-01'\", child))\n    case UnixSeconds(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"SECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMicros(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMillis(expressionExtractor(child)) =>\n      Some(\n        f(\"ROUND\",\n          op(\"/\", f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child), \"1000\")))\n    case SecondsToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"SECOND\", child, \"'1970-01-01 00:00:00'\"))\n    case MillisToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", op(\"*\", child, \"1000\"), \"'1970-01-01 00:00:00'\"))\n    case MicrosToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", child, \"'1970-01-01 00:00:00'\"))\n\n    case LengthOfJsonArray(expressionExtractor(child)) =>\n      Some(f(\"LENGTH\", f(\"JSON_TO_ARRAY\", child)))\n\n    case NextDay(expressionExtractor(startDate), utf8StringFoldableExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(\n          startDate,\n          sqlMapValueCaseInsensitive(\n            StringVar(dayOfWeek.toString),\n            DAYS_OF_WEEK_OFFSET_MAP,\n            StringVar(null)\n          )\n        ))\n\n    case NextDay(expressionExtractor(startDate), expressionExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(startDate,\n                       sqlMapValueCaseInsensitive(\n                         dayOfWeek,\n                         DAYS_OF_WEEK_OFFSET_MAP,\n                         StringVar(null)\n                       )))\n\n    case TimeAdd(expressionExtractor(start),\n                 Literal(v: Long, DayTimeIntervalType(_, _)),\n                 timeZoneId) => {\n      Some(addMicroseconds(start, v))\n    }\n\n    case TimestampAddYMInterval(expressionExtractor(start),\n                                Literal(v: Int, YearMonthIntervalType(_, _)),\n                                timeZoneId) => {\n      Some(addMonths(start, v))\n    }\n\n    case Lead(expressionExtractor(input),\n              expressionExtractor(offset),\n              Literal(null, NullType),\n              false) =>\n      Some(f(\"LEAD\", input, offset))\n    case Lag(expressionExtractor(input),\n             expressionExtractor(offset),\n             Literal(null, NullType),\n             false) =>\n      Some(f(\"LAG\", input, offset))\n\n    case BitwiseGet(expressionExtractor(left), expressionExtractor(right)) =>\n      Some(op(\"&\", op(\">>\", left, right), \"1\"))\n\n    case LikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"OR\"))\n    }\n    case NotLikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"AND\")))\n    }\n    case LikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"AND\"))\n    }\n    case NotLikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"OR\")))\n    }\n\n    case WidthBucket(expressionExtractor(value),\n                     expressionExtractor(minValue),\n                     expressionExtractor(maxValue),\n                     expressionExtractor(numBucket)) => {\n      var caseBranches = stringToJoinable(\"\")\n      // when (numBucket <= 0) or (minValue = maxValue)  then null\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"<=\", numBucket, IntVar(0)),\n        op(\"=\", minValue, maxValue),\n      ) + Raw(\"THEN \") + StringVar(null)\n\n      // when (value < minValue and minValue < maxValue) or (value > minValue and minValue > maxValue) then 0\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\"<\", value, minValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\">\", value, minValue), op(\">\", minValue, maxValue))\n      ) + Raw(\"THEN 0\")\n\n      // when (value > maxValue and minValue < maxValue) or (value < maxValue and minValue > maxValue) then numBucket + 1\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\">\", value, maxValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\"<\", value, maxValue), op(\">\", minValue, maxValue))) +\n        Raw(\"THEN\") + op(\"+\", numBucket, \"1\")\n\n      // else FLOOR( (value - minValue)*numBucket / (maxValue - minValue) ) + 1 END\n      val elseBranch = Raw(\"ELSE\") + op(\n        \"+\",\n        f(\"FLOOR\",\n          op(\"/\", op(\"*\", numBucket, op(\"-\", value, minValue)), op(\"-\", maxValue, minValue))),\n        IntVar(1)\n      )\n      Some(block(Raw(\"CASE\") + caseBranches + elseBranch + Raw(\"END\")))\n    }\n\n    // stringExpressions.scala\n    case Left(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"LEFT\", str, len))\n    case Right(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"RIGHT\", str, len))\n\n    case Round(expressionExtractor(child), expressionExtractor(scale), false)    => Some(f(\"ROUND\", child, scale))\n    case Unhex(expressionExtractor(child), false)     => Some(f(\"UNHEX\", child))\n\n    // ----------------------------------\n    // Ternary Expressions\n    // ----------------------------------\n\n    // mathExpressions.scala\n    case Conv(expressionExtractor(numExpr),\n              intFoldableExtractor(fromBase),\n              intFoldableExtractor(toBase), false)\n        // SingleStore supports bases only from [2, 36]\n        if fromBase >= 2 && fromBase <= 36 &&\n          toBase >= 2 && toBase <= 36 =>\n      Some(f(\"CONV\", numExpr, IntVar(fromBase), IntVar(toBase)))\n\n\n\n    case _ => None\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/VersionSpecificSortExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Sort}\nimport org.apache.spark.sql.execution.datasources.LogicalRelation\n\nobject VersionSpecificSortExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[SortOrder], Boolean, LogicalPlan)] =\n    plan match {\n      case s @ Sort(order, global, child) =>\n        Option(order, global, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/VersionSpecificUtil.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.types.{\n  CalendarIntervalType,\n  DataType,\n  DayTimeIntervalType,\n  YearMonthIntervalType\n}\n\nobject VersionSpecificUtil {\n  def isIntervalType(d: DataType): Boolean =\n    d.isInstanceOf[CalendarIntervalType] || d.isInstanceOf[DayTimeIntervalType] || d\n      .isInstanceOf[YearMonthIntervalType]\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/VersionSpecificWindowBoundaryExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport org.apache.spark.sql.catalyst.expressions.{Expression, UnaryMinus}\n\ncase class VersionSpecificWindowBoundaryExpressionExtractor(\n    expressionExtractor: ExpressionExtractor) {\n  def unapply(arg: Expression): Option[Statement] = {\n    arg match {\n      case UnaryMinus(expressionExtractor(child), false) =>\n        Some(child + \"PRECEDING\")\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv3.5/spark/VersionSpecificWindowExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Window}\n\nobject VersionSpecificWindowExtractor {\n  def unapply(plan: LogicalPlan)\n    : Option[(Seq[NamedExpression], Seq[Expression], Seq[SortOrder], LogicalPlan)] =\n    plan match {\n      case Window(windowExpressions, partitionSpec, orderSpec, child) =>\n        Option(windowExpressions, partitionSpec, orderSpec, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/MaxNumConcurrentTasks.scala",
    "content": "package org.apache.spark.scheduler\n\nimport org.apache.spark.rdd.RDD\n\nobject MaxNumConcurrentTasks {\n  def get(rdd: RDD[_]): Int = {\n    val (_, resourceProfiles) =\n      rdd.sparkContext.dagScheduler.getShuffleDependenciesAndResourceProfiles(rdd)\n    val resourceProfile =\n      rdd.sparkContext.dagScheduler.mergeResourceProfilesForStage(resourceProfiles)\n    rdd.sparkContext.maxNumConcurrentTasks(resourceProfile)\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/VersionSpecificAggregateExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport com.singlestore.spark.ExpressionGen.{aggregateWithFilter, f, op}\nimport org.apache.spark.sql.catalyst.expressions.aggregate.{\n  AggregateFunction,\n  Average,\n  First,\n  Kurtosis,\n  Last,\n  Skewness,\n  StddevPop,\n  StddevSamp,\n  Sum,\n  VariancePop,\n  VarianceSamp\n}\nimport org.apache.spark.sql.catalyst.expressions.EvalMode\n\ncase class VersionSpecificAggregateExpressionExtractor(expressionExtractor: ExpressionExtractor,\n                                                       context: SQLGenContext,\n                                                       filter: Option[SQLGen.Joinable]) {\n  def unapply(aggFunc: AggregateFunction): Option[Statement] = {\n    aggFunc match {\n      // CentralMomentAgg.scala\n      case StddevPop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_POP\", child, filter))\n      case StddevSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"STDDEV_SAMP\", child, filter))\n      case VariancePop(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_POP\", child, filter))\n      case VarianceSamp(expressionExtractor(child), true) =>\n        Some(aggregateWithFilter(\"VAR_SAMP\", child, filter))\n      case Kurtosis(expressionExtractor(child), true) =>\n        // ( (AVG(POW(child, 4)) - AVG(child) * POW(AVG(child), 3) * 4  + 6 * AVG(POW(child), 2) * POW(AVG(child), 2)  - 3 * POW(AVG(child), 4) )\n        //  / POW(STD(child), 4) )  - 3\n        // following the formula from https://stats.oarc.ucla.edu/other/mult-pkg/faq/general/faq-whats-with-the-different-formulas-for-kurtosis/ article\n        Some(\n          op(\n            \"-\",\n            op(\n              \"/\",\n              op(\n                \"-\",\n                op(\n                  \"+\",\n                  op(\n                    \"-\",\n                    aggregateWithFilter(\"AVG\", f(\"POW\", child, \"4\"), filter),\n                    op(\"*\",\n                       op(\"*\",\n                          aggregateWithFilter(\"AVG\", child, filter),\n                          aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter)),\n                       \"4\")\n                  ),\n                  op(\"*\",\n                     \"6\",\n                     op(\"*\",\n                        aggregateWithFilter(\"AVG\", f(\"POW\", child, \"2\"), filter),\n                        f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"2\")))\n                ),\n                op(\"*\", \"3\", f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"4\"))\n              ),\n              f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"4\")\n            ),\n            \"3\"\n          )\n        )\n\n      case Skewness(expressionExtractor(child), true) =>\n        // (AVG(POW(child, 3)) - AVG(child) * POW(STD(child), 2) * 3 - POW(AVG(child), 3) ) / POW(STD(child), 3)\n        // following the definition section in https://en.wikipedia.org/wiki/Skewness\n        Some(\n          op(\n            \"/\",\n            op(\n              \"-\",\n              op(\n                \"-\",\n                aggregateWithFilter(\"AVG\", f(\"POW\", child, \"3\"), filter),\n                op(\"*\",\n                   op(\"*\",\n                      aggregateWithFilter(\"AVG\", child, filter),\n                      f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"2\")),\n                   \"3\")\n              ),\n              f(\"POW\", aggregateWithFilter(\"AVG\", child, filter), \"3\")\n            ),\n            f(\"POW\", aggregateWithFilter(\"STD\", child, filter), \"3\")\n          )\n        )\n\n      // First.scala\n      case First(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Last.scala\n      case Last(expressionExtractor(child), false) =>\n        Some(aggregateWithFilter(\"ANY_VALUE\", child, filter))\n\n      // Sum.scala\n      case Sum(expressionExtractor(child), EvalMode.LEGACY) =>\n        Some(aggregateWithFilter(\"SUM\", child, filter))\n\n      // Average.scala\n      case Average(expressionExtractor(child), EvalMode.LEGACY) =>\n        Some(aggregateWithFilter(\"AVG\", child, filter))\n\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/VersionSpecificAggregateExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression}\nimport org.apache.spark.sql.catalyst.plans.logical.{Aggregate, LogicalPlan}\n\nobject VersionSpecificAggregateExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[Expression], Seq[NamedExpression], LogicalPlan)] =\n    plan match {\n      case a @ Aggregate(groupingExpressions, aggregateExpressions, child, _) =>\n        Option(groupingExpressions, aggregateExpressions, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/VersionSpecificExpressionGen.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.ExpressionGen._\nimport com.singlestore.spark.SQLGen.{\n  ExpressionExtractor,\n  IntVar,\n  Joinable,\n  Raw,\n  StringVar,\n  block,\n  cast,\n  sqlMapValueCaseInsensitive,\n  stringToJoinable\n}\nimport org.apache.spark.sql.catalyst.expressions._\nimport org.apache.spark.sql.catalyst.expressions.json.JsonExpressionUtils\nimport org.apache.spark.sql.catalyst.expressions.objects.StaticInvoke\nimport org.apache.spark.sql.types.{\n  BinaryType,\n  BooleanType,\n  ByteType,\n  DateType,\n  DayTimeIntervalType,\n  DecimalType,\n  DoubleType,\n  FloatType,\n  IntegerType,\n  LongType,\n  NullType,\n  ShortType,\n  StringType,\n  TimestampType,\n  YearMonthIntervalType\n}\n\ncase class VersionSpecificExpressionGen(expressionExtractor: ExpressionExtractor) {\n  def unapply(e: Expression): Option[Joinable] = e match {\n    case MakeDate(expressionExtractor(year),\n                  expressionExtractor(month),\n                  expressionExtractor(day),\n                  false) =>\n      Some(f(\"DATE\", f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day)))\n    case MakeTimestamp(expressionExtractor(year),\n                       expressionExtractor(month),\n                       expressionExtractor(day),\n                       expressionExtractor(hour),\n                       expressionExtractor(min),\n                       expressionExtractor(sec),\n                       _,\n                       _,\n                       false,\n                       TimestampType) =>\n      Some(\n        f(\"TIMESTAMP\",\n          f(\"CONCAT\", year, \"'-'\", month, \"'-'\", day, \"' '\", hour, \"':'\", min, \"':'\", sec)))\n\n    case Elt(expressionExtractor(Some(child)), false) => Some(f(\"ELT\", child))\n\n    case IntegralDivide(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(f(\"FLOOR\", op(\"/\", left, right)))\n\n    // arithmetic.scala\n    case Add(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"+\", left, right))\n    case Subtract(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"-\", left, right))\n    case Multiply(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"*\", left, right))\n    case Divide(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"/\", left, right))\n    case Remainder(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(op(\"%\", left, right))\n    case Abs(expressionExtractor(child), false) => Some(f(\"ABS\", child))\n\n    case Pmod(expressionExtractor(left), expressionExtractor(right), EvalMode.LEGACY) =>\n      Some(block(block(block(left + \"%\" + right) + \"+\" + right) + \"%\" + right))\n\n    // SingleStore and spark support other date formats\n    // UnixTime doesn't use format if time is already a dataType or TimestampType\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case ToUnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false) if e.dataType == DateType =>\n      Some(f(\"UNIX_TIMESTAMP\", timeExp))\n\n    case UnixTimestamp(e @ expressionExtractor(timeExp), _, _, false)\n        if e.dataType == TimestampType =>\n      Some(f(\"ROUND\", f(\"UNIX_TIMESTAMP\", timeExp), \"0\"))\n\n    // regexpExpression.scala\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       pos) if pos.foldable && pos.eval() == null =>\n      Some(f(\"REGEXP_REPLACE\", subject, regexp, rep, StringVar(\"g\")))\n\n    case RegExpReplace(expressionExtractor(subject),\n                       expressionExtractor(regexp),\n                       expressionExtractor(rep),\n                       intFoldableExtractor(pos)) =>\n      Some(\n        f(\"CONCAT\",\n          f(\"LEFT\", subject, IntVar(pos - 1)),\n          f(\"REGEXP_REPLACE\",\n            f(\"RIGHT\", subject, op(\"-\", f(\"LENGTH\", subject), IntVar(pos - 1))),\n            regexp,\n            rep,\n            StringVar(\"g\"))))\n\n    case UnaryMinus(expressionExtractor(child), false) => Some(f(\"-\", child))\n\n    // randomExpression.scala\n    // TODO PLAT-5759\n    case Rand(expressionExtractor(child), false) => Some(f(\"RAND\", child))\n\n    // Cast.scala\n    case Cast(e @ expressionExtractor(child), dataType, _, EvalMode.LEGACY) => {\n      dataType match {\n        case TimestampType => {\n          e.dataType match {\n            case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n                DoubleType | _: DecimalType =>\n              Some(cast(f(\"FROM_UNIXTIME\", child), \"DATETIME(6)\"))\n            case _ => Some(cast(child, \"DATETIME(6)\"))\n          }\n        }\n        case DateType => Some(cast(child, \"DATE\"))\n\n        case StringType => Some(cast(child, \"CHAR\"))\n        case BinaryType => Some(cast(child, \"BINARY\"))\n\n        case _: BooleanType | ByteType | ShortType | IntegerType | LongType | FloatType |\n            DoubleType | _: DecimalType =>\n          if (e.dataType == DateType) {\n            Some(StringVar(null))\n          } else {\n            val numeric_ch = if (e.dataType == TimestampType) {\n              f(\"UNIX_TIMESTAMP\", child)\n            } else {\n              child\n            }\n            dataType match {\n              case BooleanType     => Some(op(\"!=\", numeric_ch, IntVar(0)))\n              case ByteType        => Some(op(\"!:>\", numeric_ch, \"TINYINT\"))\n              case ShortType       => Some(op(\"!:>\", numeric_ch, \"SMALLINT\"))\n              case IntegerType     => Some(op(\"!:>\", numeric_ch, \"INT\"))\n              case LongType        => Some(op(\"!:>\", numeric_ch, \"BIGINT\"))\n              case FloatType       => Some(op(\"!:>\", numeric_ch, \"FLOAT\"))\n              case DoubleType      => Some(op(\"!:>\", numeric_ch, \"DOUBLE\"))\n              case dt: DecimalType => Some(makeDecimal(numeric_ch, dt.precision, dt.scale))\n            }\n          }\n        // SingleStore doesn't know how to handle this cast, pass it through AS is\n        case _ => Some(child)\n      }\n    }\n\n    case DateFromUnixDate(expressionExtractor(child)) => Some(f(\"FROM_UNIXTIME\", child))\n    case UnixDate(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"DAY\", \"'1970-01-01'\", child))\n    case UnixSeconds(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"SECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMicros(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child))\n    case UnixMillis(expressionExtractor(child)) =>\n      Some(\n        f(\"ROUND\",\n          op(\"/\", f(\"TIMESTAMPDIFF\", \"MICROSECOND\", \"'1970-01-01 00:00:00'\", child), \"1000\")))\n    case SecondsToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"SECOND\", child, \"'1970-01-01 00:00:00'\"))\n    case MillisToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", op(\"*\", child, \"1000\"), \"'1970-01-01 00:00:00'\"))\n    case MicrosToTimestamp(expressionExtractor(child)) =>\n      Some(f(\"TIMESTAMPADD\", \"MICROSECOND\", child, \"'1970-01-01 00:00:00'\"))\n\n    case LengthOfJsonArray(expressionExtractor(child)) =>\n      Some(f(\"LENGTH\", f(\"JSON_TO_ARRAY\", child)))\n\n    case StaticInvoke(clazz, _, \"lengthOfJsonArray\", Seq(expressionExtractor(child)), _, _, _, _, _)\n        if clazz == classOf[JsonExpressionUtils] =>\n      Some(f(\"LENGTH\", f(\"JSON_TO_ARRAY\", child)))\n\n    case NextDay(expressionExtractor(startDate), utf8StringFoldableExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(\n          startDate,\n          sqlMapValueCaseInsensitive(\n            StringVar(dayOfWeek.toString),\n            DAYS_OF_WEEK_OFFSET_MAP,\n            StringVar(null)\n          )\n        ))\n\n    case NextDay(expressionExtractor(startDate), expressionExtractor(dayOfWeek), false) =>\n      Some(\n        computeNextDay(startDate,\n                       sqlMapValueCaseInsensitive(\n                         dayOfWeek,\n                         DAYS_OF_WEEK_OFFSET_MAP,\n                         StringVar(null)\n                       )))\n\n    case TimeAdd(expressionExtractor(start),\n                 Literal(v: Long, DayTimeIntervalType(_, _)),\n                 timeZoneId) => {\n      Some(addMicroseconds(start, v))\n    }\n\n    case TimestampAddYMInterval(expressionExtractor(start),\n                                Literal(v: Int, YearMonthIntervalType(_, _)),\n                                timeZoneId) => {\n      Some(addMonths(start, v))\n    }\n\n    case Lead(expressionExtractor(input),\n              expressionExtractor(offset),\n              Literal(null, NullType),\n              false) =>\n      Some(f(\"LEAD\", input, offset))\n    case Lag(expressionExtractor(input),\n             expressionExtractor(offset),\n             Literal(null, NullType),\n             false) =>\n      Some(f(\"LAG\", input, offset))\n\n    case BitwiseGet(expressionExtractor(left), expressionExtractor(right)) =>\n      Some(op(\"&\", op(\">>\", left, right), \"1\"))\n\n    case LikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"OR\"))\n    }\n    case NotLikeAny(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"AND\")))\n    }\n    case LikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(likePatterns(child, patterns, \"AND\"))\n    }\n    case NotLikeAll(expressionExtractor(child), patterns) if patterns.size > 0 => {\n      Some(f(\"NOT\", likePatterns(child, patterns, \"OR\")))\n    }\n\n    case WidthBucket(expressionExtractor(value),\n                     expressionExtractor(minValue),\n                     expressionExtractor(maxValue),\n                     expressionExtractor(numBucket)) => {\n      var caseBranches = stringToJoinable(\"\")\n      // when (numBucket <= 0) or (minValue = maxValue)  then null\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"<=\", numBucket, IntVar(0)),\n        op(\"=\", minValue, maxValue),\n      ) + Raw(\"THEN \") + StringVar(null)\n\n      // when (value < minValue and minValue < maxValue) or (value > minValue and minValue > maxValue) then 0\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\"<\", value, minValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\">\", value, minValue), op(\">\", minValue, maxValue))\n      ) + Raw(\"THEN 0\")\n\n      // when (value > maxValue and minValue < maxValue) or (value < maxValue and minValue > maxValue) then numBucket + 1\n      caseBranches += Raw(\"WHEN\") + op(\n        \"|\",\n        op(\"&\", op(\">\", value, maxValue), op(\"<\", minValue, maxValue)),\n        op(\"&\", op(\"<\", value, maxValue), op(\">\", minValue, maxValue))) +\n        Raw(\"THEN\") + op(\"+\", numBucket, \"1\")\n\n      // else FLOOR( (value - minValue)*numBucket / (maxValue - minValue) ) + 1 END\n      val elseBranch = Raw(\"ELSE\") + op(\n        \"+\",\n        f(\"FLOOR\",\n          op(\"/\", op(\"*\", numBucket, op(\"-\", value, minValue)), op(\"-\", maxValue, minValue))),\n        IntVar(1)\n      )\n      Some(block(Raw(\"CASE\") + caseBranches + elseBranch + Raw(\"END\")))\n    }\n\n    // stringExpressions.scala\n    case Left(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"LEFT\", str, len))\n    case Right(expressionExtractor(str), expressionExtractor(len)) =>\n      Some(f(\"RIGHT\", str, len))\n\n    case Round(expressionExtractor(child), expressionExtractor(scale), false) =>\n      Some(f(\"ROUND\", child, scale))\n    case Unhex(expressionExtractor(child), false) => Some(f(\"UNHEX\", child))\n\n    // ----------------------------------\n    // Ternary Expressions\n    // ----------------------------------\n\n    // mathExpressions.scala\n    case Conv(expressionExtractor(numExpr),\n              intFoldableExtractor(fromBase),\n              intFoldableExtractor(toBase),\n              false)\n        // SingleStore supports bases only from [2, 36]\n        if fromBase >= 2 && fromBase <= 36 &&\n          toBase >= 2 && toBase <= 36 =>\n      Some(f(\"CONV\", numExpr, IntVar(fromBase), IntVar(toBase)))\n\n    case _ => None\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/VersionSpecificSortExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.SortOrder\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Sort}\n\nobject VersionSpecificSortExtractor {\n  def unapply(plan: LogicalPlan): Option[(Seq[SortOrder], Boolean, LogicalPlan)] =\n    plan match {\n      case s @ Sort(order, global, child, _) =>\n        Option(order, global, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/VersionSpecificUtil.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.types.{\n  CalendarIntervalType,\n  DataType,\n  DayTimeIntervalType,\n  YearMonthIntervalType\n}\n\nobject VersionSpecificUtil {\n  def isIntervalType(d: DataType): Boolean =\n    d.isInstanceOf[CalendarIntervalType] || d.isInstanceOf[DayTimeIntervalType] || d\n      .isInstanceOf[YearMonthIntervalType]\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/VersionSpecificWindowBoundaryExpressionExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{ExpressionExtractor, SQLGenContext, Statement}\nimport org.apache.spark.sql.catalyst.expressions.{Expression, UnaryMinus}\n\ncase class VersionSpecificWindowBoundaryExpressionExtractor(\n    expressionExtractor: ExpressionExtractor) {\n  def unapply(arg: Expression): Option[Statement] = {\n    arg match {\n      case UnaryMinus(expressionExtractor(child), false) =>\n        Some(child + \"PRECEDING\")\n      case _ => None\n    }\n  }\n}\n"
  },
  {
    "path": "src/main/scala-sparkv4.0/spark/VersionSpecificWindowExtractor.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.expressions.{Expression, NamedExpression, SortOrder}\nimport org.apache.spark.sql.catalyst.plans.logical.{LogicalPlan, Window}\n\nobject VersionSpecificWindowExtractor {\n  def unapply(plan: LogicalPlan)\n    : Option[(Seq[NamedExpression], Seq[Expression], Seq[SortOrder], LogicalPlan)] =\n    plan match {\n      case Window(windowExpressions, partitionSpec, orderSpec, child, _) =>\n        Option(windowExpressions, partitionSpec, orderSpec, child)\n      case _ => None\n    }\n}\n"
  },
  {
    "path": "src/test/resources/data/movies.json",
    "content": "{\"id\":1,\"title\":\"Vampire in Venice (Nosferatu a Venezia) (Nosferatu in Venice)\",\"genre\":\"Horror\",\"critic_review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"critic_rating\":0.2}\n{\"id\":2,\"title\":\"Friday the 13th Part 3: 3D\",\"genre\":\"Horror\",\"critic_review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"critic_rating\":2.3}\n{\"id\":3,\"title\":\"Dead Fish\",\"genre\":\"Action|Comedy|Drama|Thriller\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":4,\"title\":\"How to Die in Oregon\",\"genre\":\"Documentary|Drama\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":5,\"title\":\"47 Ronin, The (Genroku Chûshingura)\",\"genre\":\"Drama\",\"critic_review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"critic_rating\":0.8}\n{\"id\":6,\"title\":\"Tiresia\",\"genre\":\"Drama\",\"critic_review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"critic_rating\":1.5}\n{\"id\":7,\"title\":\"Crocodile Dundee\",\"genre\":\"Adventure|Comedy\",\"critic_review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"critic_rating\":5.0}\n{\"id\":8,\"title\":\"V\",\"genre\":\"Drama|Sci-Fi\",\"critic_review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"critic_rating\":1.9}\n{\"id\":9,\"title\":\"Seraphim Falls\",\"genre\":\"Action|Adventure|Drama|War|Western\",\"critic_review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"critic_rating\":2.9}\n{\"id\":10,\"title\":\"Invisible War, The\",\"genre\":\"Crime|Documentary|Drama|War\",\"critic_review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"critic_rating\":1.7}\n{\"id\":11,\"title\":\"Hard Day's Night, A\",\"genre\":\"Adventure|Comedy|Musical\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":12,\"title\":\"Hi Diddle Diddle\",\"genre\":\"Comedy\",\"critic_review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"critic_rating\":2.9}\n{\"id\":13,\"title\":\"Strange Wilderness\",\"genre\":\"Comedy\",\"critic_review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"critic_rating\":3.7}\n{\"id\":14,\"title\":\"Upperworld\",\"genre\":\"Drama\",\"critic_review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"critic_rating\":0.2}\n{\"id\":15,\"title\":\"Holy Guests (Ha-Ushpizin)\",\"genre\":\"Drama\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":16,\"title\":\"4 for Texas\",\"genre\":\"Comedy|Western\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":17,\"title\":\"Ex-Lady\",\"genre\":\"Comedy|Drama\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":18,\"title\":\"Chapayev\",\"genre\":\"Drama|War\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":19,\"title\":\"Ethan Frome\",\"genre\":\"Drama\",\"critic_review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"critic_rating\":4.4}\n{\"id\":20,\"title\":\"Patton Oswalt: My Weakness Is Strong\",\"genre\":\"Comedy\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":21,\"title\":\"Keeper, The\",\"genre\":\"Crime|Drama|Thriller\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":22,\"title\":\"Poolsite\",\"genre\":\"Crime|Drama\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":23,\"title\":\"Johnny Eager\",\"genre\":\"Crime|Drama|Film-Noir|Romance\",\"critic_review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"critic_rating\":1.7}\n{\"id\":24,\"title\":\"Lucky: No Time For Love\",\"genre\":\"Action|Adventure|Drama|Musical|Romance|Thriller|War\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":25,\"title\":\"The Man in Possession\",\"genre\":\"Comedy\",\"critic_review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"critic_rating\":4.9}\n{\"id\":26,\"title\":\"Jonas\",\"genre\":\"Comedy\",\"critic_review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"critic_rating\":3.9}\n{\"id\":27,\"title\":\"Fists in the Pocket (Pugni in tasca, I)\",\"genre\":\"Drama\",\"critic_review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"critic_rating\":2.0}\n{\"id\":28,\"title\":\"Killjoy Goes to Hell\",\"genre\":\"Comedy|Horror\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":29,\"title\":\"Juno and the Paycock\",\"genre\":\"Drama\",\"critic_review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"critic_rating\":0.7}\n{\"id\":30,\"title\":\"Road Warrior, The (Mad Max 2)\",\"genre\":\"Action|Adventure|Sci-Fi\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":31,\"title\":\"Ponette\",\"genre\":\"Drama\",\"critic_review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"critic_rating\":4.0}\n{\"id\":32,\"title\":\"Kansas City Confidential\",\"genre\":\"Crime|Drama|Film-Noir|Mystery\",\"critic_review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"critic_rating\":0.5}\n{\"id\":33,\"title\":\"Naked Weapon (Chek law dak gung)\",\"genre\":\"Action|Drama|Romance|Thriller\",\"critic_review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"critic_rating\":0.4}\n{\"id\":34,\"title\":\"Tyler Perry's Madea's Witness Protection\",\"genre\":\"Comedy\",\"critic_review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"critic_rating\":2.3}\n{\"id\":35,\"title\":\"Assault of the Sasquatch (Sasquatch Assault)\",\"genre\":\"Action|Horror\",\"critic_review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"critic_rating\":3.8}\n{\"id\":36,\"title\":\"Aloft\",\"genre\":\"Drama\",\"critic_review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"critic_rating\":0.6}\n{\"id\":37,\"title\":\"Last Year at Marienbad (L'Année dernière à Marienbad)\",\"genre\":\"Drama|Mystery|Romance\",\"critic_review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"critic_rating\":4.4}\n{\"id\":38,\"title\":\"Mr. Lucky\",\"genre\":\"Comedy|Romance\",\"critic_review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"critic_rating\":1.2}\n{\"id\":39,\"title\":\"Camila\",\"genre\":\"Drama|Romance\",\"critic_review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"critic_rating\":4.6}\n{\"id\":40,\"title\":\"Star Trek II: The Wrath of Khan\",\"genre\":\"Action|Adventure|Sci-Fi|Thriller\",\"critic_review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"critic_rating\":1.2}\n{\"id\":41,\"title\":\"Low Down, The\",\"genre\":\"Drama\",\"critic_review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"critic_rating\":1.9}\n{\"id\":42,\"title\":\"Hard Sun\",\"genre\":\"Drama|Romance\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":43,\"title\":\"Shining Through\",\"genre\":\"Drama|Romance|Thriller|War\",\"critic_review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"critic_rating\":4.2}\n{\"id\":44,\"title\":\"Missing\",\"genre\":\"Drama|Mystery|Thriller\",\"critic_review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"critic_rating\":4.6}\n{\"id\":45,\"title\":\"Free The Mind\",\"genre\":\"Documentary\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":46,\"title\":\"2AM: The Smiling Man\",\"genre\":\"Horror\",\"critic_review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"critic_rating\":3.4}\n{\"id\":47,\"title\":\"Marriage Retreat\",\"genre\":\"Comedy|Drama\",\"critic_review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"critic_rating\":2.0}\n{\"id\":48,\"title\":\"Jennifer 8\",\"genre\":\"Mystery|Thriller\",\"critic_review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"critic_rating\":4.2}\n{\"id\":49,\"title\":\"Recipes for Disaster\",\"genre\":\"Documentary\",\"critic_review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"critic_rating\":4.5}\n{\"id\":50,\"title\":\"Space Is The Place\",\"genre\":\"Sci-Fi\",\"critic_review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"critic_rating\":0.3}\n{\"id\":51,\"title\":\"César\",\"genre\":\"Comedy|Drama|Romance\",\"critic_review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"critic_rating\":0.9}\n{\"id\":52,\"title\":\"American Samurai (Ninja: American Samurai)\",\"genre\":\"Action\",\"critic_review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"critic_rating\":3.2}\n{\"id\":53,\"title\":\"Station Agent, The\",\"genre\":\"Comedy|Drama\",\"critic_review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"critic_rating\":1.5}\n{\"id\":54,\"title\":\"42nd Street\",\"genre\":\"Drama|Musical|Romance\",\"critic_review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"critic_rating\":3.7}\n{\"id\":55,\"title\":\"Asterix and the Big Fight (Astérix et le coup du menhir)\",\"genre\":\"Adventure|Animation|Children|Comedy\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":56,\"title\":\"Sweet Dreams\",\"genre\":\"Drama\",\"critic_review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"critic_rating\":0.6}\n{\"id\":57,\"title\":\"Bullets Over Broadway\",\"genre\":\"Comedy\",\"critic_review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"critic_rating\":1.9}\n{\"id\":58,\"title\":\"Choke\",\"genre\":\"Documentary\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":59,\"title\":\"Inferno\",\"genre\":\"Horror\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":60,\"title\":\"Midsummer Night's Dream, A\",\"genre\":\"Comedy|Fantasy|Romance\",\"critic_review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"critic_rating\":2.0}\n{\"id\":61,\"title\":\"Bushwhacked\",\"genre\":\"Adventure|Comedy|Crime|Mystery\",\"critic_review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"critic_rating\":0.5}\n{\"id\":62,\"title\":\"Everyone Else (Alle Anderen)\",\"genre\":\"Drama|Romance\",\"critic_review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"critic_rating\":0.3}\n{\"id\":63,\"title\":\"In the Line of Fire\",\"genre\":\"Action|Thriller\",\"critic_review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"critic_rating\":2.9}\n{\"id\":64,\"title\":\"Delirium\",\"genre\":\"Adventure|Romance|Sci-Fi\",\"critic_review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"critic_rating\":2.6}\n{\"id\":65,\"title\":\"Dumb & Dumber (Dumb and Dumber)\",\"genre\":\"Adventure|Comedy\",\"critic_review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"critic_rating\":3.9}\n{\"id\":66,\"title\":\"Mickey One\",\"genre\":\"Crime|Drama\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":67,\"title\":\"Serpent and the Rainbow, The\",\"genre\":\"Horror\",\"critic_review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"critic_rating\":2.1}\n{\"id\":68,\"title\":\"Ben-Hur\",\"genre\":\"Action|Adventure|Drama\",\"critic_review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"critic_rating\":0.0}\n{\"id\":69,\"title\":\"Village of the Damned\",\"genre\":\"Horror|Sci-Fi|Thriller\",\"critic_review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"critic_rating\":0.5}\n{\"id\":70,\"title\":\"California Split\",\"genre\":\"Comedy|Drama\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":71,\"title\":\"My Stars (Mes stars et moi)\",\"genre\":\"Comedy\",\"critic_review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"critic_rating\":4.1}\n{\"id\":72,\"title\":\"Lacombe Lucien\",\"genre\":\"Drama\",\"critic_review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"critic_rating\":4.9}\n{\"id\":73,\"title\":\"Late Quartet, A\",\"genre\":\"Drama\",\"critic_review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"critic_rating\":2.9}\n{\"id\":74,\"title\":\"Step Up All In\",\"genre\":\"Drama|Musical|Romance\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":75,\"title\":\"Duel\",\"genre\":\"Action|Mystery|Thriller\",\"critic_review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"critic_rating\":0.7}\n{\"id\":76,\"title\":\"Cremaster 2\",\"genre\":\"Drama\",\"critic_review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"critic_rating\":2.5}\n{\"id\":77,\"title\":\"Red Dawn\",\"genre\":\"Action|Drama|War\",\"critic_review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"critic_rating\":2.1}\n{\"id\":78,\"title\":\"Apartment, The\",\"genre\":\"Comedy|Drama|Romance\",\"critic_review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"critic_rating\":4.4}\n{\"id\":79,\"title\":\"Where's Marlowe?\",\"genre\":\"Comedy\",\"critic_review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"critic_rating\":4.3}\n{\"id\":80,\"title\":\"Potiche\",\"genre\":\"Comedy\",\"critic_review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"critic_rating\":1.9}\n{\"id\":81,\"title\":\"Don's Plum\",\"genre\":\"Drama\",\"critic_review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"critic_rating\":3.2}\n{\"id\":82,\"title\":\"Death Warrant\",\"genre\":\"Action\",\"critic_review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"critic_rating\":3.7}\n{\"id\":83,\"title\":\"Tenacious D in The Pick of Destiny\",\"genre\":\"Adventure|Comedy|Musical\",\"critic_review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"critic_rating\":4.9}\n{\"id\":84,\"title\":\"How to Succeed in Business Without Really Trying\",\"genre\":\"Comedy|Musical\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":85,\"title\":\"I'm Here\",\"genre\":\"Drama\",\"critic_review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"critic_rating\":2.7}\n{\"id\":86,\"title\":\"Lust for Life\",\"genre\":\"Drama\",\"critic_review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"critic_rating\":3.4}\n{\"id\":87,\"title\":\"Alien Nation: Dark Horizon\",\"genre\":\"Drama|Sci-Fi\",\"critic_review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"critic_rating\":4.4}\n{\"id\":88,\"title\":\"The Boy Who Cried Werewolf\",\"genre\":\"Children|Horror\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":89,\"title\":\"Busting\",\"genre\":\"Crime|Drama\",\"critic_review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"critic_rating\":4.5}\n{\"id\":90,\"title\":\"A Home at the End of the World\",\"genre\":\"Drama|Romance\",\"critic_review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"critic_rating\":2.4}\n{\"id\":91,\"title\":\"Two Friends\",\"genre\":\"Drama\",\"critic_review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"critic_rating\":2.7}\n{\"id\":92,\"title\":\"Hangmen Also Die\",\"genre\":\"Drama|War\",\"critic_review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"critic_rating\":3.5}\n{\"id\":93,\"title\":\"Hated\",\"genre\":\"Documentary\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":94,\"title\":\"To End All Wars\",\"genre\":\"Action|Drama|War\",\"critic_review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"critic_rating\":2.5}\n{\"id\":95,\"title\":\"White Men Can't Jump\",\"genre\":\"Comedy|Drama\",\"critic_review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"critic_rating\":4.1}\n{\"id\":96,\"title\":\"Rum Diary, The\",\"genre\":\"Comedy|Drama|Thriller\",\"critic_review\":null,\"critic_rating\":null}\n{\"id\":97,\"title\":\"Paleface, The\",\"genre\":\"Comedy\",\"critic_review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"critic_rating\":3.6}\n{\"id\":98,\"title\":\"A.C.O.D.\",\"genre\":\"Comedy\",\"critic_review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"critic_rating\":3.2}\n{\"id\":99,\"title\":\"Moving McAllister\",\"genre\":\"Comedy\",\"critic_review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"critic_rating\":4.8}\n{\"id\":100,\"title\":\"By the Law\",\"genre\":\"Drama\",\"critic_review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"critic_rating\":4.3}"
  },
  {
    "path": "src/test/resources/data/movies_rating.json",
    "content": "{\"id\":1,\"movie_rating\": '{ \\\"movie_id\\\" : 1, \\\"title\\\" : \\\"Vampire in Venice (Nosferatu a Venezia) (Nosferatu in Venice)\\\", \\\"rate\\\": 0.200000, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Tue\\\" }, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"] }',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":2,\"movie_rating\":'{ \\\"movie_id\\\" : 2, \\\"title\\\" : \\\"Friday the 13th Part 3: 3D\\\", \\\"rate\\\": 2.300000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":3,\"movie_rating\":'{ \\\"movie_id\\\" : 3, \\\"title\\\" : \\\"Dead Fish\\\", \\\"rate\\\": null, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":4,\"movie_rating\":'{ \\\"movie_id\\\" : 4, \\\"title\\\" : \\\"How to Die in Oregon\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":5,\"movie_rating\":'{ \\\"movie_id\\\" : 5, \\\"title\\\" : \\\"47 Ronin, The (Genroku Chûshingura)\\\", \\\"rate\\\": 0.800000, \\\"3D\\\": true,  \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":6,\"movie_rating\":'{ \\\"movie_id\\\" : 6, \\\"title\\\" : \\\"Tiresia\\\", \\\"rate\\\": 1.500000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":7,\"movie_rating\":'{ \\\"movie_id\\\" : 7, \\\"title\\\" : \\\"Dundee\\\", \\\"rate\\\": 5.000000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"amazing\\\", \\\"great movie\\\", \\\"highly recommend\\\"]}',\"same_rate_movies\":\"[7]\"}\n{\"id\":8,\"movie_rating\":'{ \\\"movie_id\\\" : 8, \\\"title\\\" : \\\"V\\\", \\\"rate\\\": 1.900000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":9,\"movie_rating\":'{ \\\"movie_id\\\" : 9, \\\"title\\\" : \\\"Seraphim Falls\\\", \\\"rate\\\": 2.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":10,\"movie_rating\":'{ \\\"movie_id\\\" : 10, \\\"title\\\" : \\\"Invisible War, The\\\", \\\"rate\\\": 1.700000, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":11,\"movie_rating\":'{ \\\"movie_id\\\" : 11, \\\"title\\\" : \\\"Hard Day s Night, A\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":12,\"movie_rating\":'{ \\\"movie_id\\\" : 12, \\\"title\\\" : \\\"Hi Diddle Diddle\\\", \\\"rate\\\": 2.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":13,\"movie_rating\":'{ \\\"movie_id\\\" : 13, \\\"title\\\" : \\\"Strange Wilderness\\\", \\\"rate\\\": 3.700000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 4, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":14,\"movie_rating\":'{ \\\"movie_id\\\" : 14, \\\"title\\\" : \\\"Upperworld\\\", \\\"rate\\\": 0.200000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":15,\"movie_rating\":'{ \\\"movie_id\\\" : 15, \\\"title\\\" : \\\"Holy Guests (Ha-Ushpizin)\\\", \\\"rate\\\": null, \\\"3D\\\": true,  \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":16,\"movie_rating\":'{ \\\"movie_id\\\" : 16, \\\"title\\\" : \\\"4 for Texas\\\", \\\"rate\\\": null, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":17,\"movie_rating\":'{ \\\"movie_id\\\" : 17, \\\"title\\\" : \\\"Ex-Lady\\\", \\\"rate\\\": null, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":18,\"movie_rating\":'{ \\\"movie_id\\\" : 18, \\\"title\\\" : \\\"Chapayev\\\", \\\"rate\\\": null, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 3, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":19,\"movie_rating\":'{ \\\"movie_id\\\" : 19, \\\"title\\\" : \\\"Ethan Frome\\\", \\\"rate\\\": 4.400000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":20,\"movie_rating\":'{ \\\"movie_id\\\" : 20, \\\"title\\\" : \\\"Patton Oswalt: My Weakness Is Strong\\\", \\\"rate\\\": null, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":21,\"movie_rating\":'{ \\\"movie_id\\\" : 21, \\\"title\\\" : \\\"Keeper, The\\\", \\\"rate\\\": null, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":22,\"movie_rating\":'{ \\\"movie_id\\\" : 22, \\\"title\\\" : \\\"Poolsite\\\", \\\"rate\\\": null, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 9, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":23,\"movie_rating\":'{ \\\"movie_id\\\" : 23, \\\"title\\\" : \\\"Johnny Eager\\\", \\\"rate\\\": 1.700000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":24,\"movie_rating\":'{ \\\"movie_id\\\" : 24, \\\"title\\\" : \\\"Lucky: No Time For Love\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":25,\"movie_rating\":'{ \\\"movie_id\\\" : 25, \\\"title\\\" : \\\"The Man in Possession\\\", \\\"rate\\\": 4.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":26,\"movie_rating\":'{ \\\"movie_id\\\" : 26, \\\"title\\\" : \\\"Jonas\\\", \\\"rate\\\": 3.900000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 4, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":27,\"movie_rating\":'{ \\\"movie_id\\\" : 27, \\\"title\\\" : \\\"Fists in the Pocket (Pugni in tasca, I)\\\", \\\"rate\\\": 2.000000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":28,\"movie_rating\":'{ \\\"movie_id\\\" : 28, \\\"title\\\" : \\\"Killjoy Goes to Hell\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":29,\"movie_rating\":'{ \\\"movie_id\\\" : 29, \\\"title\\\" : \\\"Juno and the Paycock\\\", \\\"rate\\\": 0.700000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":30,\"movie_rating\":'{ \\\"movie_id\\\" : 30, \\\"title\\\" : \\\"Road Warrior, The (Mad Max 2)\\\", \\\"rate\\\": null, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":31,\"movie_rating\":'{ \\\"movie_id\\\" : 31, \\\"title\\\" : \\\"Ponette\\\", \\\"rate\\\": 4.000000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":32,\"movie_rating\":'{ \\\"movie_id\\\" : 32, \\\"title\\\" : \\\"Kansas City Confidential\\\", \\\"rate\\\": 0.500000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 4, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":33,\"movie_rating\":'{ \\\"movie_id\\\" : 33, \\\"title\\\" : \\\"Naked Weapon (Chek law dak gung)\\\", \\\"rate\\\": 0.400000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":34,\"movie_rating\":'{ \\\"movie_id\\\" : 34, \\\"title\\\" : \\\"Tyler Perry\\'s Madea\\'s Witness Protection\\\", \\\"rate\\\": 2.300000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 3, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":35,\"movie_rating\":'{ \\\"movie_id\\\" : 35, \\\"title\\\" : \\\"Assault of the Sasquatch (Sasquatch Assault)\\\", \\\"rate\\\": 3.800000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":36,\"movie_rating\":'{ \\\"movie_id\\\" : 36, \\\"title\\\" : \\\"Aloft\\\", \\\"rate\\\": 0.600000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 4, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":37,\"movie_rating\":'{ \\\"movie_id\\\" : 37, \\\"title\\\" : \\\"Last Year at Marienbad (L Année dernière à Marienbad)\\\", \\\"rate\\\": 4.400000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 3, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":38,\"movie_rating\":'{ \\\"movie_id\\\" : 38, \\\"title\\\" : \\\"Mr. Lucky\\\", \\\"rate\\\": 1.200000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 9, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":39,\"movie_rating\":'{ \\\"movie_id\\\" : 39, \\\"title\\\" : \\\"Camila\\\", \\\"rate\\\": 4.600000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":40,\"movie_rating\":'{ \\\"movie_id\\\" : 40, \\\"title\\\" : \\\"Star Trek II: The Wrath of Khan\\\", \\\"rate\\\": 1.200000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 9, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":41,\"movie_rating\":'{ \\\"movie_id\\\" : 41, \\\"title\\\" : \\\"Low Down, The\\\", \\\"rate\\\": 1.900000,  \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":42,\"movie_rating\":'{ \\\"movie_id\\\" : 42, \\\"title\\\" : \\\"Hard Sun\\\", \\\"rate\\\": null, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 3, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":43,\"movie_rating\":'{ \\\"movie_id\\\" : 43, \\\"title\\\" : \\\"Shining Through\\\", \\\"rate\\\": 4.200000,  \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":44,\"movie_rating\":'{ \\\"movie_id\\\" : 44, \\\"title\\\" : \\\"Missing\\\", \\\"rate\\\": 4.600000, \\\"3D\\\": false,  \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":45,\"movie_rating\":'{ \\\"movie_id\\\" : 45, \\\"title\\\" : \\\"Free The Mind\\\", \\\"rate\\\": null, \\\"3D\\\": true,  \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":46,\"movie_rating\":'{ \\\"movie_id\\\" : 46, \\\"title\\\" : \\\"2AM: The Smiling Man\\\", \\\"rate\\\": 3.400000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 4, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":47,\"movie_rating\":'{ \\\"movie_id\\\" : 47, \\\"title\\\" : \\\"Marriage Retreat\\\", \\\"rate\\\": 2.000000, \\\"3D\\\": true,  \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":48,\"movie_rating\":'{ \\\"movie_id\\\" : 48, \\\"title\\\" : \\\"Jennifer 8\\\", \\\"rate\\\": 4.200000, \\\"3D\\\": true,  \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":49,\"movie_rating\":'{ \\\"movie_id\\\" : 49, \\\"title\\\" : \\\"Recipes for Disaster\\\", \\\"rate\\\": 4.500000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 4, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":50,\"movie_rating\":'{ \\\"movie_id\\\" : 50, \\\"title\\\" : \\\"Space Is The Place\\\", \\\"rate\\\": 0.300000,  \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":51,\"movie_rating\":'{ \\\"movie_id\\\" : 51, \\\"title\\\" : \\\"César\\\", \\\"rate\\\": 0.900000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":52,\"movie_rating\":'{ \\\"movie_id\\\" : 52, \\\"title\\\" : \\\"American Samurai (Ninja: American Samurai)\\\", \\\"rate\\\": 3.200000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":53,\"movie_rating\":'{ \\\"movie_id\\\" : 53, \\\"title\\\" : \\\"Station Agent, The\\\", \\\"rate\\\": 1.500000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":54,\"movie_rating\":'{ \\\"movie_id\\\" : 54, \\\"title\\\" : \\\"42nd Street\\\", \\\"rate\\\": 3.700000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":55,\"movie_rating\":'{ \\\"movie_id\\\" : 55, \\\"title\\\" : \\\"Asterix and the Big Fight (Astérix et le coup du menhir)\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":56,\"movie_rating\":'{ \\\"movie_id\\\" : 56, \\\"title\\\" : \\\"Sweet Dreams\\\", \\\"rate\\\": 0.600000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":57,\"movie_rating\":'{ \\\"movie_id\\\" : 57, \\\"title\\\" : \\\"Bullets Over Broadway\\\", \\\"rate\\\": 1.900000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":58,\"movie_rating\":'{ \\\"movie_id\\\" : 58, \\\"title\\\" : \\\"The choke\\\", \\\"rate\\\": null, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":59,\"movie_rating\":'{ \\\"movie_id\\\" : 59, \\\"title\\\" : \\\"Inferno\\\", \\\"rate\\\": null, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 4, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":60,\"movie_rating\":'{ \\\"movie_id\\\" : 60, \\\"title\\\" : \\\"Midsummer Night\\'s Dream, A\\\", \\\"rate\\\": 2.000000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 9, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":61,\"movie_rating\":'{ \\\"movie_id\\\" : 61, \\\"title\\\" : \\\"Bushwhacked\\\", \\\"rate\\\": 0.500000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":62,\"movie_rating\":'{ \\\"movie_id\\\" : 62, \\\"title\\\" : \\\"Everyone Else (Alle Anderen)\\\", \\\"rate\\\": 0.300000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":63,\"movie_rating\":'{ \\\"movie_id\\\" : 63, \\\"title\\\" : \\\"In the Line of Fire\\\", \\\"rate\\\": 2.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":64,\"movie_rating\":'{ \\\"movie_id\\\" : 64, \\\"title\\\" : \\\"Delirium\\\", \\\"rate\\\": 2.600000,  \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":65,\"movie_rating\":'{ \\\"movie_id\\\" : 65, \\\"title\\\" : \\\"Dumb   Dumber (Dumb and Dumber)\\\", \\\"rate\\\": 3.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 9, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":66,\"movie_rating\":'{ \\\"movie_id\\\" : 66, \\\"title\\\" : \\\"Mickey One\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":67,\"movie_rating\":'{ \\\"movie_id\\\" : 67, \\\"title\\\" : \\\"Serpent and the Rainbow, The\\\", \\\"rate\\\": 2.100000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":68,\"movie_rating\":'{ \\\"movie_id\\\" : 68, \\\"title\\\" : \\\"Ben-Hur\\\", \\\"rate\\\": 0.000000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":69,\"movie_rating\":'{ \\\"movie_id\\\" : 69, \\\"title\\\" : \\\"Village of the Damned\\\", \\\"rate\\\": 0.500000, \\\"3D\\\": true,  \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":70,\"movie_rating\":'{ \\\"movie_id\\\" : 70, \\\"title\\\" : \\\"California Split\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":71,\"movie_rating\":'{ \\\"movie_id\\\" : 71, \\\"title\\\" : \\\"My Stars (Mes stars et moi)\\\", \\\"rate\\\": 4.100000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":72,\"movie_rating\":'{ \\\"movie_id\\\" : 72, \\\"title\\\" : \\\"Lacombe Lucien\\\", \\\"rate\\\": 4.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":73,\"movie_rating\":'{ \\\"movie_id\\\" : 73, \\\"title\\\" : \\\"Late Quartet, A\\\", \\\"rate\\\": 2.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":74,\"movie_rating\":'{ \\\"movie_id\\\" : 74, \\\"title\\\" : \\\"Step Up All In\\\", \\\"rate\\\": null, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":75,\"movie_rating\":'{ \\\"movie_id\\\" : 75, \\\"title\\\" : \\\"Duel\\\", \\\"rate\\\": 0.700000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"bad\\\", \\\"awful\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[1,5,14,29,32,33,36,50,51,56,61,62,68,69,75]\"}\n{\"id\":76,\"movie_rating\":'{ \\\"movie_id\\\" : 76, \\\"title\\\" : \\\"Master\\\", \\\"rate\\\": 2.500000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":77,\"movie_rating\":'{ \\\"movie_id\\\" : 77, \\\"title\\\" : \\\"Red Dawn\\\", \\\"rate\\\": 2.100000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 3, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":78,\"movie_rating\":'{ \\\"movie_id\\\" : 78, \\\"title\\\" : \\\"Apartment\\\", \\\"rate\\\": 4.400000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":79,\"movie_rating\":'{ \\\"movie_id\\\" : 79, \\\"title\\\" : \\\"Where\\'s Marlowe?\\\", \\\"rate\\\": 4.300000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":80,\"movie_rating\":'{ \\\"movie_id\\\" : 80, \\\"title\\\" : \\\"Potiche\\\", \\\"rate\\\": 1.900000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"for one time\\\", \\\"worst film ever\\\"]}',\"same_rate_movies\":\"[6,8,10,23,38,40,41,53,57,80]\"}\n{\"id\":81,\"movie_rating\":'{ \\\"movie_id\\\" : 81, \\\"title\\\" : \\\"Don\\'s Plum\\\", \\\"rate\\\": 3.200000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":82,\"movie_rating\":'{ \\\"movie_id\\\" : 82, \\\"title\\\" : \\\"Death Warrant\\\", \\\"rate\\\": 3.700000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Fri\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":83,\"movie_rating\":'{ \\\"movie_id\\\" : 83, \\\"title\\\" : \\\"Tenacious D in The Pick of Destiny\\\", \\\"rate\\\": 4.900000,  \\\"3D\\\": false,\\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":84,\"movie_rating\":'{ \\\"movie_id\\\" : 84, \\\"title\\\" : \\\"How to Succeed in Business Without Really Trying\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":85,\"movie_rating\":'{ \\\"movie_id\\\" : 85, \\\"title\\\" : \\\"I\\'m Here\\\", \\\"rate\\\": 2.700000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":86,\"movie_rating\":'{ \\\"movie_id\\\" : 86, \\\"title\\\" : \\\"Lust for Life\\\", \\\"rate\\\": 3.400000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 10, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":87,\"movie_rating\":'{ \\\"movie_id\\\" : 87, \\\"title\\\" : \\\"Alien Nation: Dark Horizon\\\", \\\"rate\\\": 4.400000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":88,\"movie_rating\":'{ \\\"movie_id\\\" : 88, \\\"title\\\" : \\\"The Boy Who Cried Werewolf\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 3, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":89,\"movie_rating\":'{ \\\"movie_id\\\" : 89, \\\"title\\\" : \\\"Busting\\\", \\\"rate\\\": 4.500000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":90,\"movie_rating\":'{ \\\"movie_id\\\" : 90, \\\"title\\\" : \\\"A Home at the End of the World\\\", \\\"rate\\\": 2.400000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":91,\"movie_rating\":'{ \\\"movie_id\\\" : 91, \\\"title\\\" : \\\"Two Friends\\\", \\\"rate\\\": 2.700000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 7, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":92,\"movie_rating\":'{ \\\"movie_id\\\" : 92, \\\"title\\\" : \\\"Hangmen Also Die\\\", \\\"rate\\\": 3.500000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Mon\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":93,\"movie_rating\":'{ \\\"movie_id\\\" : 93, \\\"title\\\" : \\\"Hated\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 9, \\\"day\\\" : \\\"Wed\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":94,\"movie_rating\":'{ \\\"movie_id\\\" : 94, \\\"title\\\" : \\\"To End All Wars\\\", \\\"rate\\\": 2.500000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"not worth your time\\\", \\\"bad\\\", \\\"not recommend\\\"]}',\"same_rate_movies\":\"[2,9,12,27,34,47,60,63,64,67,73,76,77,85,90,91,94]\"}\n{\"id\":95,\"movie_rating\":'{ \\\"movie_id\\\" : 95, \\\"title\\\" : \\\"White Men Can t Jump\\\", \\\"rate\\\": 4.100000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 2, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":96,\"movie_rating\":'{ \\\"movie_id\\\" : 96, \\\"title\\\" : \\\"Rum Diary, The\\\", \\\"rate\\\": null, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 1, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : []}',\"same_rate_movies\":\"[3,4,11,15,16,17,18,20,21,22,24,28,30,42,45,55,58,59,66,70,74,84,88,93,96]\"}\n{\"id\":97,\"movie_rating\":'{ \\\"movie_id\\\" : 97, \\\"title\\\" : \\\"Paleface, The\\\", \\\"rate\\\": 3.600000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 6, \\\"day\\\" : \\\"Tue\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":98,\"movie_rating\":'{ \\\"movie_id\\\" : 98, \\\"title\\\" : \\\"A.C.O.D.\\\", \\\"rate\\\": 3.200000, \\\"3D\\\": true, \\\"timetable\\\" : {\\\"hall\\\" : 9, \\\"day\\\" : \\\"Sun\\\"}, \\\"reviews\\\" : [\\\"ok\\\", \\\"I do not understand this\\\", \\\"for one evening\\\", \\\"normal\\\"]}',\"same_rate_movies\":\"[13,26,35,46,52,54,65,81,82,86,92,97,98]\"}\n{\"id\":99,\"movie_rating\":'{ \\\"movie_id\\\" : 99, \\\"title\\\" : \\\"Moving McAllister\\\", \\\"rate\\\": 4.800000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 8, \\\"day\\\" : \\\"Sat\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}\n{\"id\":100,\"movie_rating\":'{ \\\"movie_id\\\" : 100, \\\"title\\\" : \\\"By the Law\\\", \\\"rate\\\": 4.300000, \\\"3D\\\": false, \\\"timetable\\\" : {\\\"hall\\\" : 5, \\\"day\\\" : \\\"Thu\\\"}, \\\"reviews\\\" : [\\\"like it\\\", \\\"great movie\\\", \\\"will see it again\\\"]}',\"same_rate_movies\":\"[19,25,31,37,39,43,44,48,49,71,72,78,79,83,87,89,95,99,100]\"}"
  },
  {
    "path": "src/test/resources/data/reviews.json",
    "content": "{\"user_id\":255,\"movie_id\":44,\"rating\":3.0,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1996-01-21T17:25:11Z\"}\n{\"user_id\":451,\"movie_id\":38,\"rating\":3.4,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1970-01-01T00:00:10Z\"}\n{\"user_id\":758,\"movie_id\":51,\"rating\":1.0,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"1991-11-04T12:28:11Z\"}\n{\"user_id\":939,\"movie_id\":66,\"rating\":1.1,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2000-04-11T13:22:30Z\"}\n{\"user_id\":43,\"movie_id\":43,\"rating\":1.1,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2003-05-31T05:34:55Z\"}\n{\"user_id\":87,\"movie_id\":84,\"rating\":3.9,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1999-06-07T14:13:01Z\"}\n{\"user_id\":133,\"movie_id\":82,\"rating\":2.2,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1994-09-18T15:29:29Z\"}\n{\"user_id\":956,\"movie_id\":39,\"rating\":1.5,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1998-08-06T21:48:21Z\"}\n{\"user_id\":697,\"movie_id\":37,\"rating\":1.1,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1997-10-06T21:05:06Z\"}\n{\"user_id\":370,\"movie_id\":94,\"rating\":3.6,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1997-08-08T15:00:43Z\"}\n{\"user_id\":683,\"movie_id\":10,\"rating\":1.5,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2001-04-30T02:09:30Z\"}\n{\"user_id\":115,\"movie_id\":65,\"rating\":1.9,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1994-06-17T19:25:42Z\"}\n{\"user_id\":64,\"movie_id\":99,\"rating\":3.3,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2006-03-05T06:28:33Z\"}\n{\"user_id\":564,\"movie_id\":62,\"rating\":1.8,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2001-06-27T21:30:50Z\"}\n{\"user_id\":462,\"movie_id\":8,\"rating\":4.6,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2004-08-11T14:52:19Z\"}\n{\"user_id\":176,\"movie_id\":76,\"rating\":1.3,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"2004-10-07T02:59:54Z\"}\n{\"user_id\":635,\"movie_id\":10,\"rating\":3.5,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1992-12-02T23:10:47Z\"}\n{\"user_id\":127,\"movie_id\":36,\"rating\":4.4,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1992-09-04T00:21:03Z\"}\n{\"user_id\":951,\"movie_id\":31,\"rating\":2.4,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2004-08-13T11:18:54Z\"}\n{\"user_id\":356,\"movie_id\":91,\"rating\":3.0,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"2004-03-16T19:05:52Z\"}\n{\"user_id\":118,\"movie_id\":97,\"rating\":4.5,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1993-06-20T06:59:32Z\"}\n{\"user_id\":411,\"movie_id\":78,\"rating\":3.4,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1990-01-26T23:29:21Z\"}\n{\"user_id\":231,\"movie_id\":95,\"rating\":1.5,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"2001-02-03T18:43:02Z\"}\n{\"user_id\":679,\"movie_id\":74,\"rating\":1.3,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2006-10-23T15:51:44Z\"}\n{\"user_id\":317,\"movie_id\":22,\"rating\":1.9,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2000-09-25T23:12:58Z\"}\n{\"user_id\":982,\"movie_id\":60,\"rating\":1.7,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2001-07-25T09:59:46Z\"}\n{\"user_id\":27,\"movie_id\":78,\"rating\":2.4,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1996-04-16T03:06:28Z\"}\n{\"user_id\":254,\"movie_id\":94,\"rating\":2.8,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"2000-05-22T19:02:22Z\"}\n{\"user_id\":407,\"movie_id\":25,\"rating\":2.9,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2000-10-03T13:33:17Z\"}\n{\"user_id\":883,\"movie_id\":12,\"rating\":2.9,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1998-06-27T01:55:02Z\"}\n{\"user_id\":554,\"movie_id\":23,\"rating\":1.6,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1991-05-07T04:17:57Z\"}\n{\"user_id\":236,\"movie_id\":1,\"rating\":4.0,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2008-11-19T08:56:16Z\"}\n{\"user_id\":866,\"movie_id\":68,\"rating\":3.9,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2009-03-03T15:07:50Z\"}\n{\"user_id\":659,\"movie_id\":93,\"rating\":2.5,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2008-03-22T09:34:11Z\"}\n{\"user_id\":796,\"movie_id\":75,\"rating\":5.0,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1992-10-05T11:59:45Z\"}\n{\"user_id\":166,\"movie_id\":58,\"rating\":1.3,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1996-02-23T04:57:03Z\"}\n{\"user_id\":191,\"movie_id\":83,\"rating\":1.2,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2006-10-15T01:55:12Z\"}\n{\"user_id\":855,\"movie_id\":91,\"rating\":2.1,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1999-12-10T04:07:36Z\"}\n{\"user_id\":741,\"movie_id\":40,\"rating\":1.2,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"2004-02-17T02:31:58Z\"}\n{\"user_id\":977,\"movie_id\":47,\"rating\":4.0,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1996-08-27T10:04:14Z\"}\n{\"user_id\":196,\"movie_id\":23,\"rating\":4.4,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2009-07-29T18:04:18Z\"}\n{\"user_id\":397,\"movie_id\":29,\"rating\":1.0,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1996-05-27T20:50:48Z\"}\n{\"user_id\":607,\"movie_id\":31,\"rating\":3.3,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1999-09-30T09:02:29Z\"}\n{\"user_id\":503,\"movie_id\":83,\"rating\":1.6,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1994-01-01T01:09:03Z\"}\n{\"user_id\":319,\"movie_id\":98,\"rating\":4.9,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2000-12-31T10:54:59Z\"}\n{\"user_id\":640,\"movie_id\":80,\"rating\":1.2,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1996-03-12T11:45:00Z\"}\n{\"user_id\":346,\"movie_id\":5,\"rating\":2.5,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1997-01-19T23:17:20Z\"}\n{\"user_id\":620,\"movie_id\":2,\"rating\":4.1,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"1998-04-14T09:29:21Z\"}\n{\"user_id\":314,\"movie_id\":2,\"rating\":4.3,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1995-12-08T13:45:31Z\"}\n{\"user_id\":459,\"movie_id\":18,\"rating\":2.8,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2000-04-12T08:45:37Z\"}\n{\"user_id\":551,\"movie_id\":5,\"rating\":2.4,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2002-04-12T20:33:04Z\"}\n{\"user_id\":935,\"movie_id\":28,\"rating\":2.3,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2008-05-19T16:09:20Z\"}\n{\"user_id\":479,\"movie_id\":57,\"rating\":4.9,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1998-06-24T16:18:38Z\"}\n{\"user_id\":536,\"movie_id\":41,\"rating\":2.3,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1991-04-13T16:30:25Z\"}\n{\"user_id\":970,\"movie_id\":99,\"rating\":1.4,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1991-03-20T15:01:05Z\"}\n{\"user_id\":434,\"movie_id\":75,\"rating\":3.9,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2008-08-20T23:38:17Z\"}\n{\"user_id\":512,\"movie_id\":82,\"rating\":3.2,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1997-05-03T09:25:00Z\"}\n{\"user_id\":512,\"movie_id\":88,\"rating\":4.6,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2001-12-04T12:25:54Z\"}\n{\"user_id\":986,\"movie_id\":34,\"rating\":4.4,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"1992-09-09T11:31:34Z\"}\n{\"user_id\":177,\"movie_id\":6,\"rating\":2.8,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2004-07-09T21:24:31Z\"}\n{\"user_id\":649,\"movie_id\":78,\"rating\":4.8,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2009-08-20T06:05:30Z\"}\n{\"user_id\":932,\"movie_id\":33,\"rating\":1.8,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2009-09-29T13:26:56Z\"}\n{\"user_id\":461,\"movie_id\":21,\"rating\":3.1,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1995-01-30T13:14:11Z\"}\n{\"user_id\":707,\"movie_id\":75,\"rating\":3.6,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2004-08-15T00:35:26Z\"}\n{\"user_id\":557,\"movie_id\":60,\"rating\":2.2,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1991-07-16T15:42:44Z\"}\n{\"user_id\":511,\"movie_id\":66,\"rating\":1.2,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2007-03-31T07:43:19Z\"}\n{\"user_id\":715,\"movie_id\":28,\"rating\":3.7,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"2006-09-22T06:35:46Z\"}\n{\"user_id\":642,\"movie_id\":100,\"rating\":5.0,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2004-02-08T13:44:57Z\"}\n{\"user_id\":312,\"movie_id\":48,\"rating\":3.1,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1998-04-21T05:28:20Z\"}\n{\"user_id\":429,\"movie_id\":33,\"rating\":4.9,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2009-10-09T13:45:44Z\"}\n{\"user_id\":181,\"movie_id\":46,\"rating\":4.0,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2006-09-07T12:28:17Z\"}\n{\"user_id\":556,\"movie_id\":19,\"rating\":2.8,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2009-03-03T17:27:54Z\"}\n{\"user_id\":506,\"movie_id\":53,\"rating\":4.9,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2001-10-27T23:37:51Z\"}\n{\"user_id\":446,\"movie_id\":48,\"rating\":2.1,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2001-09-18T00:30:44Z\"}\n{\"user_id\":319,\"movie_id\":18,\"rating\":3.0,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2006-10-07T02:32:54Z\"}\n{\"user_id\":834,\"movie_id\":28,\"rating\":1.3,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1997-09-28T22:59:09Z\"}\n{\"user_id\":10,\"movie_id\":58,\"rating\":3.8,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1992-03-27T03:00:01Z\"}\n{\"user_id\":649,\"movie_id\":87,\"rating\":3.1,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"1992-07-31T10:07:29Z\"}\n{\"user_id\":821,\"movie_id\":85,\"rating\":4.6,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1997-12-26T04:01:33Z\"}\n{\"user_id\":564,\"movie_id\":34,\"rating\":2.1,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1993-06-14T22:10:45Z\"}\n{\"user_id\":924,\"movie_id\":67,\"rating\":3.4,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2001-01-21T19:50:50Z\"}\n{\"user_id\":691,\"movie_id\":99,\"rating\":2.2,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1992-06-10T12:36:40Z\"}\n{\"user_id\":975,\"movie_id\":61,\"rating\":4.1,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1994-11-15T04:11:25Z\"}\n{\"user_id\":478,\"movie_id\":14,\"rating\":2.0,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1992-01-14T18:45:02Z\"}\n{\"user_id\":126,\"movie_id\":54,\"rating\":2.8,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1992-05-16T12:01:22Z\"}\n{\"user_id\":274,\"movie_id\":4,\"rating\":3.9,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2009-11-12T12:54:42Z\"}\n{\"user_id\":289,\"movie_id\":65,\"rating\":4.4,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2003-04-30T22:29:38Z\"}\n{\"user_id\":778,\"movie_id\":98,\"rating\":3.5,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2003-04-22T18:07:48Z\"}\n{\"user_id\":352,\"movie_id\":64,\"rating\":2.5,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1998-05-31T19:39:56Z\"}\n{\"user_id\":286,\"movie_id\":61,\"rating\":4.2,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"2003-03-25T20:07:01Z\"}\n{\"user_id\":408,\"movie_id\":94,\"rating\":4.4,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2003-07-22T17:16:48Z\"}\n{\"user_id\":195,\"movie_id\":27,\"rating\":3.3,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2001-10-18T11:37:36Z\"}\n{\"user_id\":755,\"movie_id\":61,\"rating\":1.8,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1990-04-07T20:56:27Z\"}\n{\"user_id\":427,\"movie_id\":1,\"rating\":1.5,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1996-10-25T04:14:17Z\"}\n{\"user_id\":363,\"movie_id\":56,\"rating\":4.8,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1992-06-05T08:48:17Z\"}\n{\"user_id\":629,\"movie_id\":81,\"rating\":3.1,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2005-07-06T22:34:38Z\"}\n{\"user_id\":637,\"movie_id\":46,\"rating\":1.1,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1997-07-03T23:23:55Z\"}\n{\"user_id\":554,\"movie_id\":6,\"rating\":4.2,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2004-03-21T01:15:56Z\"}\n{\"user_id\":991,\"movie_id\":33,\"rating\":1.5,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2001-03-01T00:56:57Z\"}\n{\"user_id\":483,\"movie_id\":54,\"rating\":3.0,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2002-02-21T21:02:52Z\"}\n{\"user_id\":598,\"movie_id\":4,\"rating\":4.7,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2006-10-24T14:25:26Z\"}\n{\"user_id\":902,\"movie_id\":14,\"rating\":3.4,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1996-06-14T16:39:20Z\"}\n{\"user_id\":979,\"movie_id\":6,\"rating\":2.0,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1992-02-21T01:48:04Z\"}\n{\"user_id\":762,\"movie_id\":66,\"rating\":4.4,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"1994-07-13T20:39:14Z\"}\n{\"user_id\":219,\"movie_id\":19,\"rating\":1.4,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2003-12-23T06:24:42Z\"}\n{\"user_id\":620,\"movie_id\":59,\"rating\":4.6,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1992-04-18T16:30:52Z\"}\n{\"user_id\":111,\"movie_id\":29,\"rating\":4.1,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"2001-06-07T14:40:48Z\"}\n{\"user_id\":888,\"movie_id\":15,\"rating\":1.4,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1993-02-10T00:23:12Z\"}\n{\"user_id\":436,\"movie_id\":30,\"rating\":2.8,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1997-07-20T12:01:51Z\"}\n{\"user_id\":320,\"movie_id\":30,\"rating\":4.0,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2008-03-17T05:16:06Z\"}\n{\"user_id\":722,\"movie_id\":18,\"rating\":2.6,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1999-06-26T19:26:28Z\"}\n{\"user_id\":608,\"movie_id\":83,\"rating\":3.9,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2002-02-25T19:42:18Z\"}\n{\"user_id\":958,\"movie_id\":53,\"rating\":2.5,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2005-07-25T16:04:01Z\"}\n{\"user_id\":732,\"movie_id\":20,\"rating\":2.4,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2002-07-15T08:40:37Z\"}\n{\"user_id\":311,\"movie_id\":59,\"rating\":3.6,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2005-10-05T16:31:52Z\"}\n{\"user_id\":520,\"movie_id\":78,\"rating\":1.1,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2005-02-04T03:32:25Z\"}\n{\"user_id\":717,\"movie_id\":55,\"rating\":3.4,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2007-04-13T22:19:08Z\"}\n{\"user_id\":591,\"movie_id\":21,\"rating\":1.1,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2001-10-06T05:28:10Z\"}\n{\"user_id\":59,\"movie_id\":15,\"rating\":1.1,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1992-09-13T04:54:03Z\"}\n{\"user_id\":737,\"movie_id\":36,\"rating\":4.5,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1995-02-18T13:34:05Z\"}\n{\"user_id\":353,\"movie_id\":80,\"rating\":3.3,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1998-12-05T04:46:38Z\"}\n{\"user_id\":223,\"movie_id\":68,\"rating\":1.9,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2002-03-25T23:00:47Z\"}\n{\"user_id\":726,\"movie_id\":89,\"rating\":4.7,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1992-02-01T21:10:21Z\"}\n{\"user_id\":231,\"movie_id\":49,\"rating\":3.6,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2003-02-06T06:44:34Z\"}\n{\"user_id\":481,\"movie_id\":48,\"rating\":3.9,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2005-01-17T17:52:34Z\"}\n{\"user_id\":722,\"movie_id\":11,\"rating\":3.3,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2007-09-27T09:38:49Z\"}\n{\"user_id\":944,\"movie_id\":29,\"rating\":1.3,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1994-12-15T00:00:55Z\"}\n{\"user_id\":573,\"movie_id\":2,\"rating\":4.8,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2009-12-22T14:14:15Z\"}\n{\"user_id\":301,\"movie_id\":74,\"rating\":3.2,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"2009-01-27T14:39:42Z\"}\n{\"user_id\":184,\"movie_id\":38,\"rating\":3.8,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1997-12-16T05:25:02Z\"}\n{\"user_id\":719,\"movie_id\":75,\"rating\":1.2,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2006-10-24T10:11:08Z\"}\n{\"user_id\":484,\"movie_id\":52,\"rating\":3.4,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1997-03-27T02:10:22Z\"}\n{\"user_id\":891,\"movie_id\":89,\"rating\":1.8,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1999-10-28T01:42:16Z\"}\n{\"user_id\":687,\"movie_id\":39,\"rating\":4.8,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1995-03-28T12:37:23Z\"}\n{\"user_id\":10,\"movie_id\":69,\"rating\":1.1,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1996-05-18T06:22:15Z\"}\n{\"user_id\":868,\"movie_id\":24,\"rating\":3.9,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2004-02-28T12:39:32Z\"}\n{\"user_id\":706,\"movie_id\":63,\"rating\":2.8,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2008-11-28T09:13:14Z\"}\n{\"user_id\":904,\"movie_id\":96,\"rating\":2.5,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"1992-05-04T18:15:35Z\"}\n{\"user_id\":264,\"movie_id\":27,\"rating\":1.6,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1998-04-28T14:09:22Z\"}\n{\"user_id\":828,\"movie_id\":47,\"rating\":1.9,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\",\"created\":\"2004-09-09T08:37:29Z\"}\n{\"user_id\":432,\"movie_id\":92,\"rating\":1.7,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1996-02-20T12:29:12Z\"}\n{\"user_id\":680,\"movie_id\":40,\"rating\":2.7,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2009-12-07T12:16:12Z\"}\n{\"user_id\":666,\"movie_id\":38,\"rating\":1.2,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2009-02-15T15:57:26Z\"}\n{\"user_id\":512,\"movie_id\":98,\"rating\":2.3,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1991-02-02T09:08:19Z\"}\n{\"user_id\":758,\"movie_id\":31,\"rating\":3.5,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"2002-02-08T17:55:10Z\"}\n{\"user_id\":782,\"movie_id\":64,\"rating\":2.6,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2003-07-19T19:20:13Z\"}\n{\"user_id\":761,\"movie_id\":9,\"rating\":1.2,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1999-01-12T03:33:12Z\"}\n{\"user_id\":218,\"movie_id\":90,\"rating\":2.5,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2008-01-25T07:33:39Z\"}\n{\"user_id\":856,\"movie_id\":33,\"rating\":2.7,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"1996-10-31T20:10:11Z\"}\n{\"user_id\":537,\"movie_id\":53,\"rating\":2.5,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1991-07-12T14:43:36Z\"}\n{\"user_id\":477,\"movie_id\":67,\"rating\":4.0,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2004-05-19T09:16:38Z\"}\n{\"user_id\":90,\"movie_id\":79,\"rating\":1.6,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2001-01-25T07:01:37Z\"}\n{\"user_id\":682,\"movie_id\":11,\"rating\":3.4,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2006-08-23T05:52:42Z\"}\n{\"user_id\":120,\"movie_id\":34,\"rating\":1.8,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2000-03-04T20:02:50Z\"}\n{\"user_id\":633,\"movie_id\":16,\"rating\":3.4,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1992-12-12T06:11:13Z\"}\n{\"user_id\":815,\"movie_id\":87,\"rating\":2.5,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2001-01-31T08:05:40Z\"}\n{\"user_id\":92,\"movie_id\":41,\"rating\":3.9,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1992-06-07T00:55:01Z\"}\n{\"user_id\":391,\"movie_id\":31,\"rating\":4.8,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1993-02-03T18:54:11Z\"}\n{\"user_id\":907,\"movie_id\":46,\"rating\":3.4,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1992-06-11T06:29:16Z\"}\n{\"user_id\":239,\"movie_id\":29,\"rating\":4.5,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1997-12-06T19:49:03Z\"}\n{\"user_id\":879,\"movie_id\":62,\"rating\":3.6,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2009-06-15T18:43:06Z\"}\n{\"user_id\":356,\"movie_id\":17,\"rating\":1.2,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1991-11-05T15:11:53Z\"}\n{\"user_id\":617,\"movie_id\":91,\"rating\":1.3,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1996-03-19T17:30:54Z\"}\n{\"user_id\":38,\"movie_id\":17,\"rating\":4.4,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2009-10-21T15:18:08Z\"}\n{\"user_id\":484,\"movie_id\":85,\"rating\":1.9,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2008-09-15T19:26:49Z\"}\n{\"user_id\":752,\"movie_id\":38,\"rating\":2.4,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2000-09-02T23:39:32Z\"}\n{\"user_id\":123,\"movie_id\":99,\"rating\":3.9,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2007-08-28T10:39:52Z\"}\n{\"user_id\":752,\"movie_id\":69,\"rating\":2.1,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2006-11-18T12:27:24Z\"}\n{\"user_id\":661,\"movie_id\":70,\"rating\":1.1,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2009-12-20T19:35:55Z\"}\n{\"user_id\":257,\"movie_id\":40,\"rating\":4.1,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2005-10-13T15:12:12Z\"}\n{\"user_id\":679,\"movie_id\":58,\"rating\":3.9,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1997-09-15T12:04:28Z\"}\n{\"user_id\":908,\"movie_id\":48,\"rating\":1.6,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1995-06-30T23:57:16Z\"}\n{\"user_id\":437,\"movie_id\":95,\"rating\":3.3,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2007-09-09T21:58:19Z\"}\n{\"user_id\":570,\"movie_id\":88,\"rating\":1.1,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1990-09-15T08:24:06Z\"}\n{\"user_id\":878,\"movie_id\":26,\"rating\":2.9,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2000-04-29T03:48:42Z\"}\n{\"user_id\":828,\"movie_id\":67,\"rating\":4.9,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2007-10-31T06:55:55Z\"}\n{\"user_id\":963,\"movie_id\":70,\"rating\":3.9,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2001-01-23T21:06:38Z\"}\n{\"user_id\":500,\"movie_id\":88,\"rating\":4.5,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1996-06-22T06:36:58Z\"}\n{\"user_id\":471,\"movie_id\":69,\"rating\":4.3,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1995-09-24T03:26:44Z\"}\n{\"user_id\":169,\"movie_id\":79,\"rating\":1.2,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2003-03-18T09:30:01Z\"}\n{\"user_id\":153,\"movie_id\":40,\"rating\":1.2,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1995-09-03T05:23:17Z\"}\n{\"user_id\":725,\"movie_id\":17,\"rating\":4.8,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1993-08-13T15:23:19Z\"}\n{\"user_id\":380,\"movie_id\":76,\"rating\":2.1,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1998-04-26T03:06:04Z\"}\n{\"user_id\":946,\"movie_id\":41,\"rating\":3.7,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2009-10-20T19:37:14Z\"}\n{\"user_id\":137,\"movie_id\":28,\"rating\":2.7,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2006-09-14T05:58:58Z\"}\n{\"user_id\":668,\"movie_id\":28,\"rating\":1.7,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1998-12-23T04:34:52Z\"}\n{\"user_id\":946,\"movie_id\":72,\"rating\":1.6,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1996-01-20T21:42:32Z\"}\n{\"user_id\":887,\"movie_id\":31,\"rating\":2.0,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"2009-04-18T10:17:05Z\"}\n{\"user_id\":854,\"movie_id\":80,\"rating\":4.7,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2004-01-14T21:19:03Z\"}\n{\"user_id\":660,\"movie_id\":72,\"rating\":2.7,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1994-02-15T05:30:33Z\"}\n{\"user_id\":375,\"movie_id\":33,\"rating\":1.4,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1994-05-27T00:32:26Z\"}\n{\"user_id\":913,\"movie_id\":16,\"rating\":3.8,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1993-04-02T00:03:34Z\"}\n{\"user_id\":499,\"movie_id\":23,\"rating\":5.0,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2002-09-20T09:23:14Z\"}\n{\"user_id\":33,\"movie_id\":80,\"rating\":3.2,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1993-06-21T05:27:36Z\"}\n{\"user_id\":710,\"movie_id\":80,\"rating\":2.1,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1998-03-02T15:23:08Z\"}\n{\"user_id\":304,\"movie_id\":8,\"rating\":2.4,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1991-11-16T23:59:31Z\"}\n{\"user_id\":216,\"movie_id\":66,\"rating\":3.8,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2003-05-17T03:02:08Z\"}\n{\"user_id\":421,\"movie_id\":83,\"rating\":3.3,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1994-04-28T10:05:22Z\"}\n{\"user_id\":714,\"movie_id\":37,\"rating\":4.4,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1997-02-04T13:40:14Z\"}\n{\"user_id\":480,\"movie_id\":82,\"rating\":3.2,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1994-04-17T19:45:25Z\"}\n{\"user_id\":474,\"movie_id\":77,\"rating\":3.3,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"2004-10-28T12:48:24Z\"}\n{\"user_id\":606,\"movie_id\":13,\"rating\":1.8,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2006-08-23T19:23:10Z\"}\n{\"user_id\":842,\"movie_id\":71,\"rating\":1.2,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1999-06-09T02:55:21Z\"}\n{\"user_id\":86,\"movie_id\":8,\"rating\":2.0,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2005-02-14T02:46:02Z\"}\n{\"user_id\":594,\"movie_id\":66,\"rating\":1.2,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1997-02-09T21:38:01Z\"}\n{\"user_id\":199,\"movie_id\":10,\"rating\":4.4,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2003-09-20T14:31:41Z\"}\n{\"user_id\":255,\"movie_id\":74,\"rating\":1.4,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2005-03-02T00:36:50Z\"}\n{\"user_id\":402,\"movie_id\":78,\"rating\":1.1,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1995-08-30T22:21:19Z\"}\n{\"user_id\":184,\"movie_id\":7,\"rating\":4.8,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2007-03-27T21:46:46Z\"}\n{\"user_id\":157,\"movie_id\":23,\"rating\":3.9,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1997-03-17T17:12:10Z\"}\n{\"user_id\":861,\"movie_id\":98,\"rating\":4.5,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1997-12-22T11:25:05Z\"}\n{\"user_id\":233,\"movie_id\":13,\"rating\":3.0,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2005-02-27T09:54:36Z\"}\n{\"user_id\":18,\"movie_id\":2,\"rating\":4.5,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2009-06-03T14:12:36Z\"}\n{\"user_id\":880,\"movie_id\":76,\"rating\":4.4,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"2001-04-20T22:47:41Z\"}\n{\"user_id\":257,\"movie_id\":86,\"rating\":5.0,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1997-12-15T07:37:41Z\"}\n{\"user_id\":31,\"movie_id\":35,\"rating\":3.9,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1993-08-23T08:57:42Z\"}\n{\"user_id\":993,\"movie_id\":7,\"rating\":4.6,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1992-06-11T22:22:14Z\"}\n{\"user_id\":604,\"movie_id\":67,\"rating\":1.4,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1990-09-29T12:54:30Z\"}\n{\"user_id\":697,\"movie_id\":83,\"rating\":3.5,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2005-07-09T18:11:32Z\"}\n{\"user_id\":858,\"movie_id\":86,\"rating\":1.6,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1997-09-13T04:29:42Z\"}\n{\"user_id\":885,\"movie_id\":26,\"rating\":1.1,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1998-06-27T22:22:27Z\"}\n{\"user_id\":86,\"movie_id\":44,\"rating\":2.7,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1995-10-10T15:38:10Z\"}\n{\"user_id\":406,\"movie_id\":28,\"rating\":3.2,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2000-02-06T05:10:05Z\"}\n{\"user_id\":295,\"movie_id\":97,\"rating\":3.5,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1992-01-09T19:07:49Z\"}\n{\"user_id\":502,\"movie_id\":4,\"rating\":3.1,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2001-07-28T05:11:12Z\"}\n{\"user_id\":559,\"movie_id\":9,\"rating\":1.9,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2009-09-26T21:04:44Z\"}\n{\"user_id\":836,\"movie_id\":98,\"rating\":2.3,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1991-08-09T13:17:42Z\"}\n{\"user_id\":938,\"movie_id\":69,\"rating\":3.5,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2002-11-18T14:34:45Z\"}\n{\"user_id\":403,\"movie_id\":24,\"rating\":1.0,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1990-05-25T22:44:50Z\"}\n{\"user_id\":293,\"movie_id\":80,\"rating\":3.7,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2004-02-23T20:54:23Z\"}\n{\"user_id\":454,\"movie_id\":51,\"rating\":2.1,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1997-09-20T23:07:48Z\"}\n{\"user_id\":250,\"movie_id\":19,\"rating\":2.4,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\",\"created\":\"1997-08-23T10:20:57Z\"}\n{\"user_id\":687,\"movie_id\":51,\"rating\":2.5,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2002-08-23T13:32:09Z\"}\n{\"user_id\":426,\"movie_id\":75,\"rating\":2.4,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1994-05-26T02:48:58Z\"}\n{\"user_id\":946,\"movie_id\":28,\"rating\":2.4,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2009-06-19T17:55:46Z\"}\n{\"user_id\":723,\"movie_id\":41,\"rating\":1.1,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1998-02-21T11:37:09Z\"}\n{\"user_id\":878,\"movie_id\":64,\"rating\":1.1,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2003-02-05T02:23:20Z\"}\n{\"user_id\":590,\"movie_id\":23,\"rating\":4.7,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2010-01-02T14:26:35Z\"}\n{\"user_id\":682,\"movie_id\":54,\"rating\":1.1,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2005-03-15T06:13:11Z\"}\n{\"user_id\":510,\"movie_id\":87,\"rating\":1.4,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"1997-04-21T15:59:33Z\"}\n{\"user_id\":378,\"movie_id\":73,\"rating\":2.7,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"1998-01-22T22:31:55Z\"}\n{\"user_id\":300,\"movie_id\":61,\"rating\":2.6,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"2000-06-23T22:32:45Z\"}\n{\"user_id\":764,\"movie_id\":42,\"rating\":3.3,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1993-11-18T15:28:27Z\"}\n{\"user_id\":251,\"movie_id\":82,\"rating\":1.8,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1991-11-01T11:24:44Z\"}\n{\"user_id\":72,\"movie_id\":96,\"rating\":3.4,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1997-10-24T06:03:53Z\"}\n{\"user_id\":639,\"movie_id\":40,\"rating\":3.0,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2001-01-10T12:57:12Z\"}\n{\"user_id\":19,\"movie_id\":47,\"rating\":3.5,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2003-07-17T17:19:17Z\"}\n{\"user_id\":203,\"movie_id\":49,\"rating\":4.6,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2004-01-21T19:10:02Z\"}\n{\"user_id\":348,\"movie_id\":49,\"rating\":1.6,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1994-02-05T13:34:58Z\"}\n{\"user_id\":361,\"movie_id\":50,\"rating\":1.5,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2000-07-01T04:18:30Z\"}\n{\"user_id\":85,\"movie_id\":28,\"rating\":2.0,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1993-12-05T05:32:22Z\"}\n{\"user_id\":420,\"movie_id\":54,\"rating\":2.6,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1999-06-18T21:41:42Z\"}\n{\"user_id\":683,\"movie_id\":86,\"rating\":3.8,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"1996-04-25T14:23:01Z\"}\n{\"user_id\":514,\"movie_id\":46,\"rating\":1.4,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1991-04-02T21:59:37Z\"}\n{\"user_id\":177,\"movie_id\":79,\"rating\":5.0,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1993-06-23T08:13:14Z\"}\n{\"user_id\":97,\"movie_id\":25,\"rating\":4.0,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2005-03-05T05:16:46Z\"}\n{\"user_id\":952,\"movie_id\":56,\"rating\":2.3,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2000-12-04T03:01:02Z\"}\n{\"user_id\":182,\"movie_id\":23,\"rating\":4.8,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1990-08-25T23:42:00Z\"}\n{\"user_id\":685,\"movie_id\":39,\"rating\":4.6,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2006-08-24T09:43:32Z\"}\n{\"user_id\":895,\"movie_id\":49,\"rating\":1.9,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1992-01-20T17:13:49Z\"}\n{\"user_id\":202,\"movie_id\":70,\"rating\":2.6,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1994-10-05T04:32:50Z\"}\n{\"user_id\":791,\"movie_id\":36,\"rating\":1.2,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"1992-03-04T00:12:16Z\"}\n{\"user_id\":524,\"movie_id\":40,\"rating\":4.9,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1998-05-30T22:56:56Z\"}\n{\"user_id\":720,\"movie_id\":87,\"rating\":2.7,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1997-12-13T02:58:24Z\"}\n{\"user_id\":350,\"movie_id\":53,\"rating\":4.5,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"1992-04-29T14:06:31Z\"}\n{\"user_id\":490,\"movie_id\":19,\"rating\":1.4,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2006-07-14T21:28:27Z\"}\n{\"user_id\":185,\"movie_id\":81,\"rating\":4.8,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1996-09-26T12:06:06Z\"}\n{\"user_id\":95,\"movie_id\":79,\"rating\":3.9,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2003-06-07T08:27:27Z\"}\n{\"user_id\":262,\"movie_id\":65,\"rating\":4.2,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"2000-11-28T19:17:46Z\"}\n{\"user_id\":271,\"movie_id\":75,\"rating\":1.8,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2008-09-28T11:07:53Z\"}\n{\"user_id\":270,\"movie_id\":7,\"rating\":4.0,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1994-12-10T08:34:11Z\"}\n{\"user_id\":58,\"movie_id\":20,\"rating\":2.3,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1997-11-24T02:52:57Z\"}\n{\"user_id\":198,\"movie_id\":10,\"rating\":3.1,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2006-04-23T05:37:24Z\"}\n{\"user_id\":16,\"movie_id\":57,\"rating\":2.8,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1991-03-04T19:54:22Z\"}\n{\"user_id\":690,\"movie_id\":70,\"rating\":2.2,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2002-11-12T19:10:35Z\"}\n{\"user_id\":597,\"movie_id\":32,\"rating\":3.3,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1993-04-27T03:34:47Z\"}\n{\"user_id\":254,\"movie_id\":88,\"rating\":3.9,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1991-09-29T11:56:17Z\"}\n{\"user_id\":745,\"movie_id\":85,\"rating\":2.8,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1996-11-25T15:24:57Z\"}\n{\"user_id\":85,\"movie_id\":58,\"rating\":2.7,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2004-09-29T18:00:58Z\"}\n{\"user_id\":896,\"movie_id\":85,\"rating\":2.2,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2007-06-16T18:09:57Z\"}\n{\"user_id\":515,\"movie_id\":72,\"rating\":2.1,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1992-04-30T04:00:47Z\"}\n{\"user_id\":463,\"movie_id\":30,\"rating\":3.4,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2003-04-02T18:34:02Z\"}\n{\"user_id\":928,\"movie_id\":45,\"rating\":1.2,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1998-02-24T10:02:36Z\"}\n{\"user_id\":746,\"movie_id\":64,\"rating\":2.4,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2003-08-22T01:30:20Z\"}\n{\"user_id\":12,\"movie_id\":65,\"rating\":2.9,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2001-06-06T10:57:09Z\"}\n{\"user_id\":987,\"movie_id\":28,\"rating\":4.1,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1999-02-11T21:52:16Z\"}\n{\"user_id\":379,\"movie_id\":81,\"rating\":2.5,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2002-06-30T08:57:46Z\"}\n{\"user_id\":192,\"movie_id\":20,\"rating\":4.1,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1999-05-07T19:37:16Z\"}\n{\"user_id\":388,\"movie_id\":6,\"rating\":2.4,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1996-04-11T11:48:05Z\"}\n{\"user_id\":820,\"movie_id\":44,\"rating\":4.9,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2005-03-31T22:12:01Z\"}\n{\"user_id\":927,\"movie_id\":68,\"rating\":1.8,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1990-05-28T22:05:58Z\"}\n{\"user_id\":575,\"movie_id\":1,\"rating\":4.4,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2009-05-11T18:42:53Z\"}\n{\"user_id\":648,\"movie_id\":73,\"rating\":1.8,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1998-09-25T10:02:49Z\"}\n{\"user_id\":512,\"movie_id\":68,\"rating\":1.2,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1992-09-11T21:37:41Z\"}\n{\"user_id\":281,\"movie_id\":88,\"rating\":1.8,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2002-12-08T18:36:51Z\"}\n{\"user_id\":622,\"movie_id\":70,\"rating\":3.4,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2006-04-24T18:45:50Z\"}\n{\"user_id\":318,\"movie_id\":4,\"rating\":2.9,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2005-08-19T13:59:44Z\"}\n{\"user_id\":263,\"movie_id\":38,\"rating\":1.8,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1993-08-30T10:28:23Z\"}\n{\"user_id\":217,\"movie_id\":78,\"rating\":1.7,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2002-07-19T19:27:39Z\"}\n{\"user_id\":775,\"movie_id\":21,\"rating\":2.1,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2001-05-26T02:41:04Z\"}\n{\"user_id\":280,\"movie_id\":39,\"rating\":3.1,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2007-02-27T02:24:42Z\"}\n{\"user_id\":734,\"movie_id\":99,\"rating\":4.9,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2004-12-04T19:59:35Z\"}\n{\"user_id\":858,\"movie_id\":31,\"rating\":2.0,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1995-04-13T12:23:51Z\"}\n{\"user_id\":795,\"movie_id\":89,\"rating\":3.6,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2006-01-28T05:41:45Z\"}\n{\"user_id\":231,\"movie_id\":51,\"rating\":2.4,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1997-09-03T04:10:09Z\"}\n{\"user_id\":954,\"movie_id\":71,\"rating\":2.3,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"1994-07-01T18:37:06Z\"}\n{\"user_id\":306,\"movie_id\":35,\"rating\":2.2,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2008-11-23T07:37:13Z\"}\n{\"user_id\":186,\"movie_id\":57,\"rating\":4.1,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2003-11-29T09:59:37Z\"}\n{\"user_id\":811,\"movie_id\":13,\"rating\":3.4,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2007-11-10T17:59:15Z\"}\n{\"user_id\":395,\"movie_id\":67,\"rating\":4.9,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1994-06-18T04:01:34Z\"}\n{\"user_id\":550,\"movie_id\":24,\"rating\":1.2,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1999-05-19T18:30:36Z\"}\n{\"user_id\":209,\"movie_id\":73,\"rating\":1.4,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2000-03-01T09:19:14Z\"}\n{\"user_id\":798,\"movie_id\":34,\"rating\":1.8,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1997-12-25T23:35:38Z\"}\n{\"user_id\":72,\"movie_id\":17,\"rating\":1.6,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2003-08-18T17:30:01Z\"}\n{\"user_id\":2,\"movie_id\":66,\"rating\":1.2,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1996-10-03T16:02:30Z\"}\n{\"user_id\":755,\"movie_id\":22,\"rating\":4.5,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1998-05-07T05:54:59Z\"}\n{\"user_id\":217,\"movie_id\":60,\"rating\":2.6,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1990-02-10T04:08:46Z\"}\n{\"user_id\":266,\"movie_id\":100,\"rating\":3.5,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1996-05-27T01:04:21Z\"}\n{\"user_id\":222,\"movie_id\":28,\"rating\":3.8,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1996-12-08T17:22:21Z\"}\n{\"user_id\":456,\"movie_id\":12,\"rating\":2.0,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1999-05-10T21:31:10Z\"}\n{\"user_id\":374,\"movie_id\":83,\"rating\":3.5,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2005-05-16T21:05:23Z\"}\n{\"user_id\":579,\"movie_id\":67,\"rating\":3.8,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2001-02-04T14:22:45Z\"}\n{\"user_id\":112,\"movie_id\":92,\"rating\":1.0,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2004-03-31T21:38:47Z\"}\n{\"user_id\":338,\"movie_id\":34,\"rating\":4.4,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1997-12-26T18:48:36Z\"}\n{\"user_id\":726,\"movie_id\":38,\"rating\":1.3,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2008-06-16T05:50:36Z\"}\n{\"user_id\":183,\"movie_id\":27,\"rating\":1.3,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2004-08-01T12:50:55Z\"}\n{\"user_id\":614,\"movie_id\":29,\"rating\":1.1,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1993-10-06T23:46:21Z\"}\n{\"user_id\":299,\"movie_id\":48,\"rating\":4.3,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1994-05-16T23:15:32Z\"}\n{\"user_id\":196,\"movie_id\":20,\"rating\":2.9,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1990-05-06T07:27:44Z\"}\n{\"user_id\":461,\"movie_id\":65,\"rating\":2.9,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2009-10-05T19:38:34Z\"}\n{\"user_id\":510,\"movie_id\":28,\"rating\":2.3,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2002-06-11T13:20:36Z\"}\n{\"user_id\":561,\"movie_id\":15,\"rating\":2.3,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1996-11-18T13:11:35Z\"}\n{\"user_id\":449,\"movie_id\":24,\"rating\":1.4,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1996-12-02T23:34:08Z\"}\n{\"user_id\":861,\"movie_id\":40,\"rating\":2.3,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2006-11-29T17:50:53Z\"}\n{\"user_id\":542,\"movie_id\":80,\"rating\":4.2,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1998-03-17T07:02:57Z\"}\n{\"user_id\":269,\"movie_id\":73,\"rating\":1.8,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1993-05-18T21:02:39Z\"}\n{\"user_id\":159,\"movie_id\":19,\"rating\":1.8,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1990-03-04T09:09:58Z\"}\n{\"user_id\":495,\"movie_id\":61,\"rating\":2.0,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"2008-03-24T17:02:58Z\"}\n{\"user_id\":72,\"movie_id\":45,\"rating\":2.8,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2004-01-21T02:44:13Z\"}\n{\"user_id\":226,\"movie_id\":90,\"rating\":4.2,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1996-09-20T01:58:35Z\"}\n{\"user_id\":312,\"movie_id\":29,\"rating\":2.2,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"2005-07-30T17:37:15Z\"}\n{\"user_id\":599,\"movie_id\":93,\"rating\":2.7,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1994-03-07T21:12:36Z\"}\n{\"user_id\":920,\"movie_id\":1,\"rating\":2.1,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"1995-12-23T22:04:16Z\"}\n{\"user_id\":182,\"movie_id\":4,\"rating\":4.3,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1993-02-28T16:58:49Z\"}\n{\"user_id\":723,\"movie_id\":87,\"rating\":3.8,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1990-06-24T19:57:13Z\"}\n{\"user_id\":570,\"movie_id\":27,\"rating\":2.9,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1991-11-30T12:26:29Z\"}\n{\"user_id\":603,\"movie_id\":82,\"rating\":1.5,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1998-08-30T01:13:49Z\"}\n{\"user_id\":177,\"movie_id\":26,\"rating\":4.8,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"1992-02-27T13:33:52Z\"}\n{\"user_id\":630,\"movie_id\":85,\"rating\":1.8,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2009-09-13T17:51:25Z\"}\n{\"user_id\":932,\"movie_id\":49,\"rating\":3.1,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1992-03-17T03:26:21Z\"}\n{\"user_id\":998,\"movie_id\":48,\"rating\":4.0,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1992-11-19T00:19:13Z\"}\n{\"user_id\":904,\"movie_id\":25,\"rating\":2.2,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\",\"created\":\"2008-02-28T23:15:48Z\"}\n{\"user_id\":991,\"movie_id\":75,\"rating\":3.2,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1997-10-28T22:06:25Z\"}\n{\"user_id\":11,\"movie_id\":39,\"rating\":2.1,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1995-11-01T06:31:41Z\"}\n{\"user_id\":577,\"movie_id\":87,\"rating\":4.8,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2008-05-14T04:43:32Z\"}\n{\"user_id\":827,\"movie_id\":45,\"rating\":4.9,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1990-01-24T15:05:22Z\"}\n{\"user_id\":581,\"movie_id\":42,\"rating\":4.5,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2002-01-09T02:26:12Z\"}\n{\"user_id\":545,\"movie_id\":46,\"rating\":3.8,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2000-03-28T23:31:58Z\"}\n{\"user_id\":616,\"movie_id\":60,\"rating\":1.6,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2008-12-20T21:03:14Z\"}\n{\"user_id\":144,\"movie_id\":66,\"rating\":2.4,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1996-03-13T13:03:22Z\"}\n{\"user_id\":324,\"movie_id\":7,\"rating\":3.0,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2006-08-07T11:02:59Z\"}\n{\"user_id\":57,\"movie_id\":52,\"rating\":3.4,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2002-07-29T12:26:54Z\"}\n{\"user_id\":379,\"movie_id\":67,\"rating\":3.4,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2008-10-05T04:55:15Z\"}\n{\"user_id\":737,\"movie_id\":90,\"rating\":1.5,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1997-12-03T03:28:16Z\"}\n{\"user_id\":915,\"movie_id\":63,\"rating\":2.7,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1991-09-26T06:57:45Z\"}\n{\"user_id\":851,\"movie_id\":1,\"rating\":4.7,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2001-11-15T10:35:28Z\"}\n{\"user_id\":52,\"movie_id\":4,\"rating\":4.6,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2000-01-01T08:06:42Z\"}\n{\"user_id\":1,\"movie_id\":18,\"rating\":3.1,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2004-12-29T01:52:37Z\"}\n{\"user_id\":40,\"movie_id\":54,\"rating\":1.8,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"2006-05-05T18:13:04Z\"}\n{\"user_id\":869,\"movie_id\":21,\"rating\":3.8,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1998-12-04T16:26:31Z\"}\n{\"user_id\":326,\"movie_id\":57,\"rating\":4.2,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2008-12-17T03:18:49Z\"}\n{\"user_id\":57,\"movie_id\":18,\"rating\":3.1,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1998-07-24T14:21:02Z\"}\n{\"user_id\":392,\"movie_id\":99,\"rating\":4.4,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2000-05-21T20:33:13Z\"}\n{\"user_id\":597,\"movie_id\":10,\"rating\":4.7,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1998-04-27T03:17:00Z\"}\n{\"user_id\":233,\"movie_id\":54,\"rating\":2.8,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2007-11-15T02:50:40Z\"}\n{\"user_id\":124,\"movie_id\":16,\"rating\":2.3,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1999-01-27T11:17:02Z\"}\n{\"user_id\":23,\"movie_id\":21,\"rating\":2.8,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1998-06-14T06:45:57Z\"}\n{\"user_id\":698,\"movie_id\":35,\"rating\":5.0,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2001-06-10T18:01:44Z\"}\n{\"user_id\":139,\"movie_id\":83,\"rating\":2.6,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2005-06-10T11:06:11Z\"}\n{\"user_id\":82,\"movie_id\":45,\"rating\":2.1,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1995-12-28T19:37:18Z\"}\n{\"user_id\":864,\"movie_id\":95,\"rating\":4.9,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1992-05-20T12:50:05Z\"}\n{\"user_id\":90,\"movie_id\":65,\"rating\":2.6,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1991-08-09T16:52:42Z\"}\n{\"user_id\":250,\"movie_id\":82,\"rating\":1.1,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1995-02-26T02:27:28Z\"}\n{\"user_id\":884,\"movie_id\":96,\"rating\":3.1,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2001-03-11T04:17:47Z\"}\n{\"user_id\":810,\"movie_id\":88,\"rating\":3.8,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1997-03-27T04:11:00Z\"}\n{\"user_id\":311,\"movie_id\":78,\"rating\":1.1,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2009-12-07T19:06:22Z\"}\n{\"user_id\":911,\"movie_id\":22,\"rating\":1.3,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1997-10-20T01:42:13Z\"}\n{\"user_id\":6,\"movie_id\":46,\"rating\":1.6,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"2009-05-16T12:26:48Z\"}\n{\"user_id\":430,\"movie_id\":96,\"rating\":2.7,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2007-01-17T02:43:40Z\"}\n{\"user_id\":813,\"movie_id\":56,\"rating\":4.1,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1999-11-27T11:39:11Z\"}\n{\"user_id\":773,\"movie_id\":96,\"rating\":4.4,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2002-06-29T12:15:29Z\"}\n{\"user_id\":843,\"movie_id\":98,\"rating\":1.5,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2004-10-03T18:50:32Z\"}\n{\"user_id\":612,\"movie_id\":7,\"rating\":3.2,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2005-08-20T15:30:25Z\"}\n{\"user_id\":292,\"movie_id\":96,\"rating\":2.6,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1991-05-27T10:31:55Z\"}\n{\"user_id\":16,\"movie_id\":76,\"rating\":2.9,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1997-06-28T22:09:57Z\"}\n{\"user_id\":721,\"movie_id\":47,\"rating\":4.4,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1998-04-27T18:51:11Z\"}\n{\"user_id\":502,\"movie_id\":11,\"rating\":3.8,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2004-02-15T13:47:58Z\"}\n{\"user_id\":351,\"movie_id\":84,\"rating\":1.5,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2006-06-29T11:14:57Z\"}\n{\"user_id\":976,\"movie_id\":94,\"rating\":1.5,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1990-07-05T05:01:24Z\"}\n{\"user_id\":87,\"movie_id\":54,\"rating\":1.3,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1999-10-18T09:11:40Z\"}\n{\"user_id\":639,\"movie_id\":4,\"rating\":4.3,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1996-06-29T12:14:36Z\"}\n{\"user_id\":656,\"movie_id\":32,\"rating\":1.6,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2005-07-05T08:45:36Z\"}\n{\"user_id\":597,\"movie_id\":59,\"rating\":3.8,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2005-09-29T06:38:06Z\"}\n{\"user_id\":577,\"movie_id\":12,\"rating\":3.4,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1999-09-16T20:27:07Z\"}\n{\"user_id\":780,\"movie_id\":70,\"rating\":1.8,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1993-12-29T13:17:35Z\"}\n{\"user_id\":980,\"movie_id\":61,\"rating\":3.1,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2002-03-22T09:24:07Z\"}\n{\"user_id\":295,\"movie_id\":30,\"rating\":1.7,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2007-11-11T05:43:15Z\"}\n{\"user_id\":193,\"movie_id\":6,\"rating\":4.4,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1996-06-08T17:08:43Z\"}\n{\"user_id\":965,\"movie_id\":38,\"rating\":4.9,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2001-07-23T18:29:14Z\"}\n{\"user_id\":77,\"movie_id\":68,\"rating\":3.0,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1993-11-06T09:10:49Z\"}\n{\"user_id\":185,\"movie_id\":5,\"rating\":3.3,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2008-06-17T23:06:34Z\"}\n{\"user_id\":317,\"movie_id\":99,\"rating\":3.0,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2001-10-26T17:32:42Z\"}\n{\"user_id\":56,\"movie_id\":12,\"rating\":3.7,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2002-04-06T07:02:21Z\"}\n{\"user_id\":731,\"movie_id\":67,\"rating\":3.7,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2006-09-17T07:10:27Z\"}\n{\"user_id\":191,\"movie_id\":12,\"rating\":3.2,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2007-08-15T10:58:19Z\"}\n{\"user_id\":564,\"movie_id\":54,\"rating\":4.7,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2000-05-16T02:16:18Z\"}\n{\"user_id\":148,\"movie_id\":1,\"rating\":2.4,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1998-02-25T00:43:10Z\"}\n{\"user_id\":118,\"movie_id\":57,\"rating\":4.3,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2003-11-25T04:55:17Z\"}\n{\"user_id\":187,\"movie_id\":84,\"rating\":1.5,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1992-09-05T00:06:19Z\"}\n{\"user_id\":682,\"movie_id\":41,\"rating\":1.3,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2004-01-31T10:48:26Z\"}\n{\"user_id\":631,\"movie_id\":71,\"rating\":3.0,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1991-02-17T20:28:01Z\"}\n{\"user_id\":379,\"movie_id\":43,\"rating\":4.2,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1993-04-13T17:06:15Z\"}\n{\"user_id\":583,\"movie_id\":42,\"rating\":4.1,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2003-01-05T18:08:39Z\"}\n{\"user_id\":188,\"movie_id\":49,\"rating\":1.8,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2008-03-05T03:52:08Z\"}\n{\"user_id\":86,\"movie_id\":60,\"rating\":3.6,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1994-07-31T22:44:59Z\"}\n{\"user_id\":250,\"movie_id\":4,\"rating\":1.5,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2001-10-09T22:33:33Z\"}\n{\"user_id\":93,\"movie_id\":19,\"rating\":3.4,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2008-06-14T23:07:12Z\"}\n{\"user_id\":388,\"movie_id\":91,\"rating\":4.1,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2006-11-13T14:31:37Z\"}\n{\"user_id\":632,\"movie_id\":22,\"rating\":2.7,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1995-09-15T01:09:52Z\"}\n{\"user_id\":837,\"movie_id\":16,\"rating\":3.1,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2004-03-19T21:45:23Z\"}\n{\"user_id\":624,\"movie_id\":7,\"rating\":2.2,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1991-05-04T12:20:13Z\"}\n{\"user_id\":81,\"movie_id\":51,\"rating\":3.4,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"1996-04-25T04:25:52Z\"}\n{\"user_id\":349,\"movie_id\":58,\"rating\":1.4,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2006-06-21T17:27:25Z\"}\n{\"user_id\":905,\"movie_id\":30,\"rating\":4.1,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\",\"created\":\"1991-07-22T18:26:43Z\"}\n{\"user_id\":755,\"movie_id\":60,\"rating\":4.7,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1993-02-28T18:00:07Z\"}\n{\"user_id\":726,\"movie_id\":88,\"rating\":2.9,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2005-08-25T16:47:46Z\"}\n{\"user_id\":655,\"movie_id\":87,\"rating\":3.8,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1992-10-14T15:44:31Z\"}\n{\"user_id\":268,\"movie_id\":71,\"rating\":3.1,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1990-03-21T07:25:27Z\"}\n{\"user_id\":706,\"movie_id\":81,\"rating\":3.6,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1998-06-19T02:59:51Z\"}\n{\"user_id\":997,\"movie_id\":73,\"rating\":1.6,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2002-10-19T20:03:50Z\"}\n{\"user_id\":821,\"movie_id\":100,\"rating\":3.0,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2007-02-07T16:02:35Z\"}\n{\"user_id\":305,\"movie_id\":23,\"rating\":4.8,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1994-02-07T16:35:48Z\"}\n{\"user_id\":852,\"movie_id\":18,\"rating\":1.2,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1998-11-15T05:46:56Z\"}\n{\"user_id\":440,\"movie_id\":90,\"rating\":4.5,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1997-12-27T20:11:23Z\"}\n{\"user_id\":425,\"movie_id\":13,\"rating\":3.9,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1992-09-24T21:52:31Z\"}\n{\"user_id\":765,\"movie_id\":11,\"rating\":1.8,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1995-08-29T15:55:51Z\"}\n{\"user_id\":626,\"movie_id\":72,\"rating\":4.6,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1999-09-09T01:29:51Z\"}\n{\"user_id\":682,\"movie_id\":35,\"rating\":2.7,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1993-10-19T10:57:34Z\"}\n{\"user_id\":646,\"movie_id\":63,\"rating\":4.0,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1998-07-27T03:31:44Z\"}\n{\"user_id\":594,\"movie_id\":3,\"rating\":3.8,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1994-09-27T11:40:46Z\"}\n{\"user_id\":880,\"movie_id\":75,\"rating\":1.0,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1997-06-18T09:12:55Z\"}\n{\"user_id\":399,\"movie_id\":96,\"rating\":1.5,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1992-10-02T20:42:04Z\"}\n{\"user_id\":485,\"movie_id\":98,\"rating\":4.4,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2000-09-03T19:52:57Z\"}\n{\"user_id\":507,\"movie_id\":87,\"rating\":2.2,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1991-04-03T14:22:33Z\"}\n{\"user_id\":404,\"movie_id\":78,\"rating\":3.6,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2000-09-08T15:10:54Z\"}\n{\"user_id\":261,\"movie_id\":10,\"rating\":1.9,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2007-12-26T07:21:45Z\"}\n{\"user_id\":519,\"movie_id\":64,\"rating\":2.4,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2008-03-08T09:54:25Z\"}\n{\"user_id\":277,\"movie_id\":75,\"rating\":3.7,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1999-04-14T19:36:46Z\"}\n{\"user_id\":768,\"movie_id\":57,\"rating\":3.0,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2006-05-29T22:14:14Z\"}\n{\"user_id\":772,\"movie_id\":47,\"rating\":3.8,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2009-05-12T01:23:49Z\"}\n{\"user_id\":623,\"movie_id\":7,\"rating\":5.0,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2000-02-10T11:57:23Z\"}\n{\"user_id\":261,\"movie_id\":56,\"rating\":2.4,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2004-06-23T20:36:11Z\"}\n{\"user_id\":103,\"movie_id\":64,\"rating\":3.1,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1994-07-22T10:27:59Z\"}\n{\"user_id\":720,\"movie_id\":69,\"rating\":2.0,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1995-09-17T10:38:00Z\"}\n{\"user_id\":794,\"movie_id\":29,\"rating\":1.5,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2006-08-22T19:46:14Z\"}\n{\"user_id\":594,\"movie_id\":14,\"rating\":2.3,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2001-07-11T21:45:54Z\"}\n{\"user_id\":193,\"movie_id\":43,\"rating\":1.2,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1990-04-11T01:34:16Z\"}\n{\"user_id\":281,\"movie_id\":67,\"rating\":3.4,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1995-12-13T06:13:19Z\"}\n{\"user_id\":708,\"movie_id\":14,\"rating\":3.6,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1993-10-06T08:26:01Z\"}\n{\"user_id\":561,\"movie_id\":22,\"rating\":4.1,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"2003-01-31T01:40:16Z\"}\n{\"user_id\":742,\"movie_id\":8,\"rating\":2.1,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2007-03-08T06:54:11Z\"}\n{\"user_id\":294,\"movie_id\":20,\"rating\":4.4,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1996-09-18T23:54:59Z\"}\n{\"user_id\":709,\"movie_id\":15,\"rating\":4.0,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1992-11-05T13:19:36Z\"}\n{\"user_id\":424,\"movie_id\":10,\"rating\":2.8,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2005-12-18T19:21:41Z\"}\n{\"user_id\":353,\"movie_id\":44,\"rating\":3.1,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"2009-05-19T11:24:55Z\"}\n{\"user_id\":140,\"movie_id\":35,\"rating\":2.0,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1998-05-30T03:56:18Z\"}\n{\"user_id\":167,\"movie_id\":7,\"rating\":4.3,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2003-05-16T04:42:32Z\"}\n{\"user_id\":96,\"movie_id\":10,\"rating\":4.8,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1991-04-24T15:18:49Z\"}\n{\"user_id\":385,\"movie_id\":47,\"rating\":1.6,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1997-01-24T06:29:32Z\"}\n{\"user_id\":376,\"movie_id\":83,\"rating\":4.9,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2005-10-29T00:31:45Z\"}\n{\"user_id\":976,\"movie_id\":61,\"rating\":3.1,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2004-12-07T06:14:20Z\"}\n{\"user_id\":344,\"movie_id\":65,\"rating\":2.4,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2003-07-04T14:25:11Z\"}\n{\"user_id\":909,\"movie_id\":12,\"rating\":5.0,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1992-01-02T05:36:19Z\"}\n{\"user_id\":952,\"movie_id\":50,\"rating\":1.6,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2003-12-23T23:34:49Z\"}\n{\"user_id\":64,\"movie_id\":52,\"rating\":4.0,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1992-02-03T17:58:55Z\"}\n{\"user_id\":82,\"movie_id\":40,\"rating\":1.7,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2007-02-10T00:57:52Z\"}\n{\"user_id\":888,\"movie_id\":56,\"rating\":1.1,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2006-01-25T20:18:58Z\"}\n{\"user_id\":269,\"movie_id\":65,\"rating\":1.2,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1994-09-26T16:17:40Z\"}\n{\"user_id\":743,\"movie_id\":90,\"rating\":2.0,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2000-09-28T15:37:07Z\"}\n{\"user_id\":873,\"movie_id\":29,\"rating\":1.1,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2000-11-16T23:04:17Z\"}\n{\"user_id\":208,\"movie_id\":67,\"rating\":3.3,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2008-12-22T20:39:20Z\"}\n{\"user_id\":620,\"movie_id\":51,\"rating\":2.3,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2003-07-29T06:32:05Z\"}\n{\"user_id\":543,\"movie_id\":34,\"rating\":3.9,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"2003-10-28T03:00:26Z\"}\n{\"user_id\":917,\"movie_id\":41,\"rating\":1.5,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2000-03-17T11:49:49Z\"}\n{\"user_id\":968,\"movie_id\":21,\"rating\":4.7,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1992-07-06T02:16:33Z\"}\n{\"user_id\":64,\"movie_id\":90,\"rating\":2.7,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1996-05-10T11:00:58Z\"}\n{\"user_id\":756,\"movie_id\":87,\"rating\":3.0,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2004-09-07T02:17:25Z\"}\n{\"user_id\":569,\"movie_id\":87,\"rating\":2.8,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1995-09-24T01:06:59Z\"}\n{\"user_id\":245,\"movie_id\":55,\"rating\":3.7,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2009-04-23T12:42:02Z\"}\n{\"user_id\":699,\"movie_id\":10,\"rating\":4.4,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1992-03-19T02:11:55Z\"}\n{\"user_id\":143,\"movie_id\":38,\"rating\":3.3,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2006-11-18T23:16:38Z\"}\n{\"user_id\":18,\"movie_id\":94,\"rating\":3.6,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2001-01-11T23:23:42Z\"}\n{\"user_id\":392,\"movie_id\":56,\"rating\":4.0,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1998-08-06T15:08:56Z\"}\n{\"user_id\":999,\"movie_id\":97,\"rating\":4.3,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1994-01-11T21:06:20Z\"}\n{\"user_id\":667,\"movie_id\":86,\"rating\":2.1,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2003-06-24T13:30:39Z\"}\n{\"user_id\":419,\"movie_id\":75,\"rating\":2.0,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1990-10-16T18:18:39Z\"}\n{\"user_id\":593,\"movie_id\":99,\"rating\":3.9,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2004-05-26T05:08:02Z\"}\n{\"user_id\":169,\"movie_id\":83,\"rating\":3.4,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2004-09-13T16:16:54Z\"}\n{\"user_id\":5,\"movie_id\":59,\"rating\":2.5,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"1998-11-14T12:27:01Z\"}\n{\"user_id\":834,\"movie_id\":53,\"rating\":3.4,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1996-02-12T11:23:10Z\"}\n{\"user_id\":831,\"movie_id\":61,\"rating\":5.0,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2000-08-03T20:45:40Z\"}\n{\"user_id\":344,\"movie_id\":74,\"rating\":1.7,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1991-12-22T10:12:44Z\"}\n{\"user_id\":660,\"movie_id\":62,\"rating\":2.1,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1991-10-14T13:22:48Z\"}\n{\"user_id\":892,\"movie_id\":47,\"rating\":3.3,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2008-03-03T05:10:13Z\"}\n{\"user_id\":278,\"movie_id\":60,\"rating\":3.8,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1993-03-14T10:53:20Z\"}\n{\"user_id\":234,\"movie_id\":3,\"rating\":1.3,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2006-04-07T10:26:01Z\"}\n{\"user_id\":979,\"movie_id\":97,\"rating\":4.6,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1990-09-13T03:56:41Z\"}\n{\"user_id\":278,\"movie_id\":96,\"rating\":1.5,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1993-04-03T05:15:32Z\"}\n{\"user_id\":150,\"movie_id\":55,\"rating\":2.7,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1999-09-07T06:44:45Z\"}\n{\"user_id\":94,\"movie_id\":70,\"rating\":4.3,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2007-07-30T20:17:17Z\"}\n{\"user_id\":114,\"movie_id\":57,\"rating\":3.5,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2003-08-22T12:46:14Z\"}\n{\"user_id\":718,\"movie_id\":11,\"rating\":4.4,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1992-09-18T00:26:13Z\"}\n{\"user_id\":926,\"movie_id\":98,\"rating\":2.6,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2003-10-30T21:49:08Z\"}\n{\"user_id\":652,\"movie_id\":91,\"rating\":2.5,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2009-07-24T22:19:26Z\"}\n{\"user_id\":462,\"movie_id\":39,\"rating\":2.0,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2000-12-04T15:16:50Z\"}\n{\"user_id\":865,\"movie_id\":17,\"rating\":3.8,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1993-12-28T19:02:07Z\"}\n{\"user_id\":633,\"movie_id\":22,\"rating\":4.4,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1994-02-09T15:19:14Z\"}\n{\"user_id\":478,\"movie_id\":52,\"rating\":2.7,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1999-04-11T20:24:44Z\"}\n{\"user_id\":488,\"movie_id\":12,\"rating\":4.3,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1990-04-24T01:52:15Z\"}\n{\"user_id\":294,\"movie_id\":95,\"rating\":2.6,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2004-11-08T07:18:16Z\"}\n{\"user_id\":159,\"movie_id\":47,\"rating\":4.2,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1991-02-04T05:03:31Z\"}\n{\"user_id\":251,\"movie_id\":88,\"rating\":3.2,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1998-06-25T04:14:59Z\"}\n{\"user_id\":517,\"movie_id\":89,\"rating\":1.1,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2001-10-29T21:38:56Z\"}\n{\"user_id\":350,\"movie_id\":63,\"rating\":3.2,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1992-11-27T01:51:57Z\"}\n{\"user_id\":7,\"movie_id\":9,\"rating\":3.7,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2009-06-07T22:56:03Z\"}\n{\"user_id\":288,\"movie_id\":22,\"rating\":3.5,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"1997-11-06T14:35:45Z\"}\n{\"user_id\":63,\"movie_id\":64,\"rating\":1.8,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1992-09-08T04:25:59Z\"}\n{\"user_id\":258,\"movie_id\":70,\"rating\":1.4,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1996-01-15T17:08:03Z\"}\n{\"user_id\":841,\"movie_id\":83,\"rating\":4.5,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2005-04-26T20:17:41Z\"}\n{\"user_id\":850,\"movie_id\":95,\"rating\":4.7,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2002-12-21T03:45:02Z\"}\n{\"user_id\":340,\"movie_id\":49,\"rating\":2.2,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1996-06-03T00:20:56Z\"}\n{\"user_id\":633,\"movie_id\":28,\"rating\":4.1,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1992-06-30T13:02:39Z\"}\n{\"user_id\":69,\"movie_id\":21,\"rating\":1.4,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2006-05-20T10:35:25Z\"}\n{\"user_id\":963,\"movie_id\":21,\"rating\":4.7,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2002-09-13T03:02:36Z\"}\n{\"user_id\":6,\"movie_id\":91,\"rating\":2.6,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"2005-03-24T20:48:21Z\"}\n{\"user_id\":736,\"movie_id\":75,\"rating\":2.9,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2004-02-21T19:15:18Z\"}\n{\"user_id\":605,\"movie_id\":12,\"rating\":1.2,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2004-09-08T01:51:25Z\"}\n{\"user_id\":174,\"movie_id\":97,\"rating\":4.2,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1993-11-20T11:08:43Z\"}\n{\"user_id\":849,\"movie_id\":81,\"rating\":2.9,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2004-04-20T06:37:53Z\"}\n{\"user_id\":769,\"movie_id\":29,\"rating\":3.4,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1990-08-14T23:24:15Z\"}\n{\"user_id\":954,\"movie_id\":46,\"rating\":3.4,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1994-09-08T11:45:43Z\"}\n{\"user_id\":598,\"movie_id\":76,\"rating\":3.0,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2005-10-11T08:38:52Z\"}\n{\"user_id\":344,\"movie_id\":69,\"rating\":3.0,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1997-04-08T18:41:22Z\"}\n{\"user_id\":170,\"movie_id\":46,\"rating\":4.6,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1992-07-21T23:17:40Z\"}\n{\"user_id\":809,\"movie_id\":35,\"rating\":4.7,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2005-06-07T09:27:30Z\"}\n{\"user_id\":848,\"movie_id\":75,\"rating\":2.4,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1996-08-16T06:16:07Z\"}\n{\"user_id\":829,\"movie_id\":89,\"rating\":1.0,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1991-08-22T03:29:11Z\"}\n{\"user_id\":852,\"movie_id\":2,\"rating\":2.4,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1993-11-21T07:38:09Z\"}\n{\"user_id\":279,\"movie_id\":3,\"rating\":2.6,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2002-12-29T19:07:42Z\"}\n{\"user_id\":97,\"movie_id\":50,\"rating\":1.7,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2008-11-23T10:20:49Z\"}\n{\"user_id\":573,\"movie_id\":69,\"rating\":1.6,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1991-06-27T13:54:01Z\"}\n{\"user_id\":994,\"movie_id\":15,\"rating\":4.9,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1999-03-28T09:13:51Z\"}\n{\"user_id\":125,\"movie_id\":35,\"rating\":3.8,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2000-02-03T04:59:19Z\"}\n{\"user_id\":667,\"movie_id\":6,\"rating\":3.9,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"2006-05-29T03:07:36Z\"}\n{\"user_id\":874,\"movie_id\":30,\"rating\":2.2,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2003-07-24T05:48:29Z\"}\n{\"user_id\":672,\"movie_id\":94,\"rating\":2.3,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2008-05-29T13:52:54Z\"}\n{\"user_id\":957,\"movie_id\":5,\"rating\":2.9,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2007-12-16T16:31:08Z\"}\n{\"user_id\":323,\"movie_id\":62,\"rating\":1.2,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2001-05-10T12:57:39Z\"}\n{\"user_id\":838,\"movie_id\":13,\"rating\":1.7,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2006-03-15T21:18:58Z\"}\n{\"user_id\":871,\"movie_id\":51,\"rating\":3.9,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\",\"created\":\"1992-05-02T02:35:05Z\"}\n{\"user_id\":67,\"movie_id\":64,\"rating\":3.7,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1993-11-28T18:18:59Z\"}\n{\"user_id\":162,\"movie_id\":26,\"rating\":1.5,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2006-12-24T17:44:30Z\"}\n{\"user_id\":870,\"movie_id\":62,\"rating\":3.5,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1999-11-05T06:28:21Z\"}\n{\"user_id\":729,\"movie_id\":89,\"rating\":4.4,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1992-08-02T03:27:05Z\"}\n{\"user_id\":278,\"movie_id\":87,\"rating\":3.7,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2005-10-26T13:59:39Z\"}\n{\"user_id\":379,\"movie_id\":90,\"rating\":3.9,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1993-03-03T16:54:33Z\"}\n{\"user_id\":244,\"movie_id\":18,\"rating\":2.1,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2000-03-18T23:04:36Z\"}\n{\"user_id\":516,\"movie_id\":74,\"rating\":1.5,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"1997-05-30T14:53:35Z\"}\n{\"user_id\":998,\"movie_id\":71,\"rating\":5.0,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2001-05-24T04:57:37Z\"}\n{\"user_id\":434,\"movie_id\":50,\"rating\":1.3,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1998-07-18T00:46:35Z\"}\n{\"user_id\":993,\"movie_id\":72,\"rating\":3.5,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1991-05-12T11:04:41Z\"}\n{\"user_id\":813,\"movie_id\":36,\"rating\":4.9,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1995-11-14T22:01:18Z\"}\n{\"user_id\":676,\"movie_id\":47,\"rating\":3.6,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2008-05-08T10:30:24Z\"}\n{\"user_id\":540,\"movie_id\":33,\"rating\":3.0,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1998-03-08T14:49:49Z\"}\n{\"user_id\":251,\"movie_id\":99,\"rating\":3.2,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1996-06-26T20:39:40Z\"}\n{\"user_id\":6,\"movie_id\":72,\"rating\":1.3,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1990-11-22T20:17:38Z\"}\n{\"user_id\":685,\"movie_id\":35,\"rating\":2.3,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1994-01-27T04:44:57Z\"}\n{\"user_id\":735,\"movie_id\":76,\"rating\":4.7,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1994-04-24T13:22:28Z\"}\n{\"user_id\":386,\"movie_id\":80,\"rating\":3.1,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1992-07-10T10:32:56Z\"}\n{\"user_id\":692,\"movie_id\":60,\"rating\":3.0,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1990-10-04T09:52:01Z\"}\n{\"user_id\":259,\"movie_id\":6,\"rating\":4.2,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2001-03-19T04:20:50Z\"}\n{\"user_id\":707,\"movie_id\":34,\"rating\":1.3,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2005-03-10T14:52:23Z\"}\n{\"user_id\":524,\"movie_id\":35,\"rating\":4.4,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1997-10-08T13:38:13Z\"}\n{\"user_id\":397,\"movie_id\":56,\"rating\":1.8,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1999-03-31T17:26:40Z\"}\n{\"user_id\":561,\"movie_id\":58,\"rating\":4.8,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2005-02-17T15:21:36Z\"}\n{\"user_id\":95,\"movie_id\":63,\"rating\":2.1,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2008-05-10T11:15:44Z\"}\n{\"user_id\":491,\"movie_id\":40,\"rating\":4.9,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2000-10-04T08:57:47Z\"}\n{\"user_id\":992,\"movie_id\":57,\"rating\":1.5,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1999-08-13T02:17:38Z\"}\n{\"user_id\":701,\"movie_id\":65,\"rating\":2.1,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2004-09-07T00:07:44Z\"}\n{\"user_id\":248,\"movie_id\":77,\"rating\":2.9,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"1994-11-25T12:04:41Z\"}\n{\"user_id\":801,\"movie_id\":3,\"rating\":2.0,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1997-08-13T02:44:53Z\"}\n{\"user_id\":874,\"movie_id\":15,\"rating\":4.9,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1996-04-21T16:54:01Z\"}\n{\"user_id\":50,\"movie_id\":37,\"rating\":4.1,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2009-09-11T12:28:04Z\"}\n{\"user_id\":366,\"movie_id\":17,\"rating\":2.8,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2006-05-04T21:03:46Z\"}\n{\"user_id\":575,\"movie_id\":82,\"rating\":2.2,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1993-07-31T20:52:41Z\"}\n{\"user_id\":193,\"movie_id\":79,\"rating\":2.5,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"2005-08-10T19:03:37Z\"}\n{\"user_id\":219,\"movie_id\":56,\"rating\":4.3,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1996-01-08T17:52:53Z\"}\n{\"user_id\":834,\"movie_id\":50,\"rating\":3.7,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1991-04-12T23:57:41Z\"}\n{\"user_id\":860,\"movie_id\":88,\"rating\":1.9,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2008-07-14T01:34:55Z\"}\n{\"user_id\":892,\"movie_id\":88,\"rating\":3.5,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2000-05-13T00:37:09Z\"}\n{\"user_id\":197,\"movie_id\":31,\"rating\":3.6,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1993-01-10T14:03:04Z\"}\n{\"user_id\":526,\"movie_id\":17,\"rating\":4.1,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1998-05-14T16:50:00Z\"}\n{\"user_id\":613,\"movie_id\":11,\"rating\":4.2,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2007-05-18T08:39:52Z\"}\n{\"user_id\":231,\"movie_id\":46,\"rating\":3.2,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"2001-07-15T08:54:34Z\"}\n{\"user_id\":726,\"movie_id\":68,\"rating\":3.6,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2004-04-08T20:07:31Z\"}\n{\"user_id\":240,\"movie_id\":94,\"rating\":2.1,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1990-09-29T12:53:40Z\"}\n{\"user_id\":235,\"movie_id\":29,\"rating\":3.6,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1997-01-13T11:23:47Z\"}\n{\"user_id\":346,\"movie_id\":74,\"rating\":3.6,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1990-03-06T23:00:55Z\"}\n{\"user_id\":309,\"movie_id\":53,\"rating\":1.2,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"1990-05-19T20:54:25Z\"}\n{\"user_id\":460,\"movie_id\":48,\"rating\":4.0,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1994-03-29T17:04:09Z\"}\n{\"user_id\":30,\"movie_id\":33,\"rating\":2.5,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1995-02-10T22:49:59Z\"}\n{\"user_id\":806,\"movie_id\":95,\"rating\":3.5,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1999-03-06T04:08:16Z\"}\n{\"user_id\":297,\"movie_id\":13,\"rating\":2.1,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"2002-07-09T13:26:32Z\"}\n{\"user_id\":96,\"movie_id\":81,\"rating\":2.4,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1994-05-30T14:02:03Z\"}\n{\"user_id\":576,\"movie_id\":30,\"rating\":3.1,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2002-07-24T14:03:15Z\"}\n{\"user_id\":774,\"movie_id\":46,\"rating\":5.0,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2008-09-04T18:53:59Z\"}\n{\"user_id\":930,\"movie_id\":65,\"rating\":1.4,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1994-03-13T13:52:02Z\"}\n{\"user_id\":105,\"movie_id\":14,\"rating\":2.4,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1999-02-22T12:12:32Z\"}\n{\"user_id\":187,\"movie_id\":10,\"rating\":5.0,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1999-03-22T16:45:20Z\"}\n{\"user_id\":236,\"movie_id\":42,\"rating\":3.2,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1990-08-17T19:18:30Z\"}\n{\"user_id\":793,\"movie_id\":70,\"rating\":3.3,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1996-01-08T00:27:24Z\"}\n{\"user_id\":680,\"movie_id\":74,\"rating\":1.4,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1999-06-15T15:38:54Z\"}\n{\"user_id\":132,\"movie_id\":7,\"rating\":4.2,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1998-10-26T21:48:50Z\"}\n{\"user_id\":500,\"movie_id\":99,\"rating\":3.9,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2001-01-04T14:16:51Z\"}\n{\"user_id\":504,\"movie_id\":53,\"rating\":1.5,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1999-01-28T22:14:13Z\"}\n{\"user_id\":669,\"movie_id\":6,\"rating\":2.9,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2006-10-27T09:03:43Z\"}\n{\"user_id\":885,\"movie_id\":25,\"rating\":2.1,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1995-07-20T20:20:07Z\"}\n{\"user_id\":123,\"movie_id\":40,\"rating\":4.8,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1998-07-12T21:26:41Z\"}\n{\"user_id\":39,\"movie_id\":69,\"rating\":3.3,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1993-04-18T05:36:42Z\"}\n{\"user_id\":554,\"movie_id\":82,\"rating\":3.1,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2002-11-10T09:54:35Z\"}\n{\"user_id\":237,\"movie_id\":26,\"rating\":2.6,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1995-05-26T05:33:57Z\"}\n{\"user_id\":355,\"movie_id\":9,\"rating\":1.2,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2008-05-05T18:02:18Z\"}\n{\"user_id\":853,\"movie_id\":13,\"rating\":1.6,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1995-02-15T00:35:51Z\"}\n{\"user_id\":427,\"movie_id\":44,\"rating\":3.1,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1998-11-02T17:29:06Z\"}\n{\"user_id\":838,\"movie_id\":70,\"rating\":1.3,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"2007-07-06T11:18:41Z\"}\n{\"user_id\":423,\"movie_id\":27,\"rating\":3.4,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1995-10-22T21:49:29Z\"}\n{\"user_id\":509,\"movie_id\":6,\"rating\":1.9,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2000-12-26T13:33:12Z\"}\n{\"user_id\":161,\"movie_id\":55,\"rating\":1.5,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1990-03-25T19:29:32Z\"}\n{\"user_id\":969,\"movie_id\":69,\"rating\":2.8,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2009-10-19T13:20:18Z\"}\n{\"user_id\":238,\"movie_id\":57,\"rating\":3.4,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1996-11-22T03:33:08Z\"}\n{\"user_id\":871,\"movie_id\":18,\"rating\":2.7,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2003-03-03T19:12:56Z\"}\n{\"user_id\":807,\"movie_id\":5,\"rating\":2.0,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1995-04-10T11:56:38Z\"}\n{\"user_id\":982,\"movie_id\":21,\"rating\":5.0,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2006-07-10T05:48:24Z\"}\n{\"user_id\":989,\"movie_id\":98,\"rating\":2.3,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1995-02-05T14:13:01Z\"}\n{\"user_id\":249,\"movie_id\":83,\"rating\":4.7,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1996-01-20T00:35:00Z\"}\n{\"user_id\":565,\"movie_id\":74,\"rating\":2.0,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1990-10-26T03:14:58Z\"}\n{\"user_id\":891,\"movie_id\":76,\"rating\":2.7,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1999-04-28T06:05:45Z\"}\n{\"user_id\":538,\"movie_id\":13,\"rating\":4.7,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2009-10-24T11:09:15Z\"}\n{\"user_id\":619,\"movie_id\":72,\"rating\":2.4,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2006-04-09T18:25:15Z\"}\n{\"user_id\":427,\"movie_id\":82,\"rating\":2.4,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"2002-08-10T10:45:48Z\"}\n{\"user_id\":468,\"movie_id\":26,\"rating\":1.6,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2002-02-04T17:26:38Z\"}\n{\"user_id\":651,\"movie_id\":61,\"rating\":1.4,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1992-11-15T00:59:08Z\"}\n{\"user_id\":3,\"movie_id\":38,\"rating\":4.9,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1995-01-11T09:00:07Z\"}\n{\"user_id\":369,\"movie_id\":8,\"rating\":1.2,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"2008-12-08T20:03:51Z\"}\n{\"user_id\":857,\"movie_id\":12,\"rating\":1.9,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1999-09-28T17:23:17Z\"}\n{\"user_id\":2,\"movie_id\":93,\"rating\":2.6,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1992-02-25T21:07:41Z\"}\n{\"user_id\":497,\"movie_id\":75,\"rating\":3.3,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1998-02-14T21:21:26Z\"}\n{\"user_id\":696,\"movie_id\":43,\"rating\":1.8,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"1999-08-01T13:55:49Z\"}\n{\"user_id\":338,\"movie_id\":40,\"rating\":1.3,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2009-07-11T22:35:30Z\"}\n{\"user_id\":122,\"movie_id\":32,\"rating\":4.0,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2009-10-15T22:16:12Z\"}\n{\"user_id\":937,\"movie_id\":97,\"rating\":3.1,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"1991-07-24T11:09:16Z\"}\n{\"user_id\":497,\"movie_id\":70,\"rating\":1.2,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1990-07-02T21:41:28Z\"}\n{\"user_id\":168,\"movie_id\":14,\"rating\":2.1,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2003-03-29T15:33:02Z\"}\n{\"user_id\":90,\"movie_id\":16,\"rating\":2.0,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1992-08-06T23:56:14Z\"}\n{\"user_id\":69,\"movie_id\":71,\"rating\":3.7,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1990-10-23T23:02:49Z\"}\n{\"user_id\":521,\"movie_id\":97,\"rating\":2.2,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1999-04-17T12:21:06Z\"}\n{\"user_id\":107,\"movie_id\":32,\"rating\":2.6,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2001-05-07T06:09:15Z\"}\n{\"user_id\":49,\"movie_id\":91,\"rating\":1.8,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1998-07-08T23:33:10Z\"}\n{\"user_id\":423,\"movie_id\":53,\"rating\":2.8,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2000-05-19T08:44:05Z\"}\n{\"user_id\":409,\"movie_id\":18,\"rating\":3.5,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"2005-09-04T23:33:12Z\"}\n{\"user_id\":253,\"movie_id\":66,\"rating\":3.8,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2008-07-21T12:38:04Z\"}\n{\"user_id\":218,\"movie_id\":66,\"rating\":4.9,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1991-05-23T08:08:50Z\"}\n{\"user_id\":469,\"movie_id\":18,\"rating\":3.0,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1998-01-19T14:19:18Z\"}\n{\"user_id\":14,\"movie_id\":77,\"rating\":3.8,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"1995-05-18T18:26:49Z\"}\n{\"user_id\":489,\"movie_id\":25,\"rating\":3.0,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"1990-02-04T02:49:44Z\"}\n{\"user_id\":290,\"movie_id\":17,\"rating\":4.2,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"1996-05-29T23:53:53Z\"}\n{\"user_id\":110,\"movie_id\":94,\"rating\":1.5,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2007-01-01T23:34:12Z\"}\n{\"user_id\":251,\"movie_id\":19,\"rating\":2.7,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1993-10-20T09:17:14Z\"}\n{\"user_id\":76,\"movie_id\":67,\"rating\":4.4,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2006-07-27T21:19:02Z\"}\n{\"user_id\":827,\"movie_id\":47,\"rating\":2.2,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2009-02-21T00:43:48Z\"}\n{\"user_id\":82,\"movie_id\":88,\"rating\":3.5,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2006-07-08T12:25:18Z\"}\n{\"user_id\":815,\"movie_id\":46,\"rating\":2.6,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1993-11-25T05:02:26Z\"}\n{\"user_id\":209,\"movie_id\":83,\"rating\":4.6,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2000-07-07T02:41:41Z\"}\n{\"user_id\":211,\"movie_id\":46,\"rating\":2.5,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1990-07-21T22:05:02Z\"}\n{\"user_id\":900,\"movie_id\":42,\"rating\":2.5,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1993-10-27T13:28:50Z\"}\n{\"user_id\":160,\"movie_id\":86,\"rating\":2.5,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2002-06-03T09:31:16Z\"}\n{\"user_id\":22,\"movie_id\":22,\"rating\":2.3,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2005-09-29T23:01:11Z\"}\n{\"user_id\":708,\"movie_id\":74,\"rating\":4.3,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2006-02-14T13:58:53Z\"}\n{\"user_id\":376,\"movie_id\":33,\"rating\":3.2,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2005-01-12T11:40:02Z\"}\n{\"user_id\":604,\"movie_id\":57,\"rating\":2.0,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2006-10-06T20:20:21Z\"}\n{\"user_id\":535,\"movie_id\":62,\"rating\":3.5,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2009-11-11T09:50:07Z\"}\n{\"user_id\":360,\"movie_id\":35,\"rating\":2.5,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1991-08-22T16:49:11Z\"}\n{\"user_id\":689,\"movie_id\":33,\"rating\":1.1,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"2006-04-09T12:36:11Z\"}\n{\"user_id\":232,\"movie_id\":79,\"rating\":2.3,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1992-05-25T03:42:28Z\"}\n{\"user_id\":32,\"movie_id\":6,\"rating\":4.4,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2003-08-03T06:06:10Z\"}\n{\"user_id\":326,\"movie_id\":64,\"rating\":3.2,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1995-09-14T14:06:06Z\"}\n{\"user_id\":863,\"movie_id\":75,\"rating\":1.5,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2005-11-08T12:39:53Z\"}\n{\"user_id\":788,\"movie_id\":21,\"rating\":4.8,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2003-06-14T15:06:26Z\"}\n{\"user_id\":582,\"movie_id\":97,\"rating\":4.7,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2005-04-29T11:06:15Z\"}\n{\"user_id\":848,\"movie_id\":68,\"rating\":1.7,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1992-01-24T09:56:06Z\"}\n{\"user_id\":200,\"movie_id\":3,\"rating\":4.1,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2009-05-17T15:01:08Z\"}\n{\"user_id\":12,\"movie_id\":19,\"rating\":2.5,\"review\":\"Quisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1992-05-09T23:04:25Z\"}\n{\"user_id\":475,\"movie_id\":38,\"rating\":4.9,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1991-02-21T02:00:09Z\"}\n{\"user_id\":86,\"movie_id\":43,\"rating\":1.3,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1993-05-24T20:11:50Z\"}\n{\"user_id\":60,\"movie_id\":97,\"rating\":4.4,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1991-10-16T08:15:48Z\"}\n{\"user_id\":479,\"movie_id\":73,\"rating\":2.7,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2003-02-11T14:41:35Z\"}\n{\"user_id\":653,\"movie_id\":20,\"rating\":2.8,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2001-04-28T14:04:17Z\"}\n{\"user_id\":765,\"movie_id\":47,\"rating\":1.9,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2007-12-21T20:27:29Z\"}\n{\"user_id\":380,\"movie_id\":41,\"rating\":1.4,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2009-04-27T10:10:35Z\"}\n{\"user_id\":153,\"movie_id\":50,\"rating\":2.6,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2003-01-13T02:04:07Z\"}\n{\"user_id\":583,\"movie_id\":41,\"rating\":5.0,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"1993-04-22T15:49:08Z\"}\n{\"user_id\":306,\"movie_id\":29,\"rating\":4.1,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1993-09-19T13:41:01Z\"}\n{\"user_id\":730,\"movie_id\":34,\"rating\":3.7,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1999-04-22T00:12:06Z\"}\n{\"user_id\":208,\"movie_id\":82,\"rating\":3.8,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2002-11-25T18:05:59Z\"}\n{\"user_id\":889,\"movie_id\":32,\"rating\":1.3,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1998-09-02T11:28:47Z\"}\n{\"user_id\":2,\"movie_id\":16,\"rating\":3.8,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1994-04-07T05:21:14Z\"}\n{\"user_id\":708,\"movie_id\":81,\"rating\":4.1,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2003-12-28T09:38:40Z\"}\n{\"user_id\":720,\"movie_id\":14,\"rating\":1.5,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2003-06-29T18:15:22Z\"}\n{\"user_id\":155,\"movie_id\":97,\"rating\":3.3,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1993-10-17T23:38:48Z\"}\n{\"user_id\":612,\"movie_id\":87,\"rating\":1.9,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2000-06-24T21:52:53Z\"}\n{\"user_id\":445,\"movie_id\":40,\"rating\":3.9,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1991-08-16T13:11:31Z\"}\n{\"user_id\":105,\"movie_id\":76,\"rating\":1.1,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2001-07-18T23:22:01Z\"}\n{\"user_id\":529,\"movie_id\":67,\"rating\":2.9,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2000-10-27T20:31:35Z\"}\n{\"user_id\":213,\"movie_id\":81,\"rating\":2.9,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1996-08-06T14:19:59Z\"}\n{\"user_id\":627,\"movie_id\":98,\"rating\":3.3,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2007-07-19T03:27:40Z\"}\n{\"user_id\":264,\"movie_id\":20,\"rating\":2.2,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2006-12-12T13:29:34Z\"}\n{\"user_id\":857,\"movie_id\":12,\"rating\":4.2,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1996-07-21T19:15:51Z\"}\n{\"user_id\":206,\"movie_id\":12,\"rating\":3.3,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1992-04-02T18:56:12Z\"}\n{\"user_id\":636,\"movie_id\":69,\"rating\":4.6,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1991-07-13T11:14:53Z\"}\n{\"user_id\":617,\"movie_id\":58,\"rating\":2.1,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2000-06-26T05:44:06Z\"}\n{\"user_id\":979,\"movie_id\":45,\"rating\":3.4,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2002-07-30T20:02:28Z\"}\n{\"user_id\":914,\"movie_id\":83,\"rating\":4.4,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1993-06-01T10:46:45Z\"}\n{\"user_id\":200,\"movie_id\":18,\"rating\":1.6,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1996-10-05T06:29:26Z\"}\n{\"user_id\":139,\"movie_id\":66,\"rating\":3.0,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"1996-01-15T10:18:11Z\"}\n{\"user_id\":10,\"movie_id\":76,\"rating\":2.4,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"2008-01-20T21:58:56Z\"}\n{\"user_id\":944,\"movie_id\":33,\"rating\":3.8,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2007-08-17T02:08:26Z\"}\n{\"user_id\":900,\"movie_id\":99,\"rating\":3.2,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1992-01-18T06:07:56Z\"}\n{\"user_id\":656,\"movie_id\":92,\"rating\":3.7,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1997-02-09T01:53:37Z\"}\n{\"user_id\":599,\"movie_id\":45,\"rating\":1.8,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2007-12-31T17:17:56Z\"}\n{\"user_id\":137,\"movie_id\":73,\"rating\":3.7,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"2005-06-24T12:14:56Z\"}\n{\"user_id\":448,\"movie_id\":50,\"rating\":1.3,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2005-04-16T16:51:13Z\"}\n{\"user_id\":713,\"movie_id\":1,\"rating\":1.5,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2008-01-11T01:45:10Z\"}\n{\"user_id\":351,\"movie_id\":17,\"rating\":2.9,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2006-04-26T14:18:01Z\"}\n{\"user_id\":778,\"movie_id\":13,\"rating\":4.1,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2001-07-12T06:38:38Z\"}\n{\"user_id\":420,\"movie_id\":77,\"rating\":2.4,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1997-02-03T18:55:19Z\"}\n{\"user_id\":592,\"movie_id\":86,\"rating\":3.6,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1994-03-22T03:28:01Z\"}\n{\"user_id\":884,\"movie_id\":46,\"rating\":4.6,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1992-05-18T06:28:27Z\"}\n{\"user_id\":934,\"movie_id\":12,\"rating\":1.4,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1993-12-11T14:57:27Z\"}\n{\"user_id\":717,\"movie_id\":55,\"rating\":2.6,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1995-12-21T03:55:13Z\"}\n{\"user_id\":203,\"movie_id\":48,\"rating\":4.9,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1994-01-10T13:19:32Z\"}\n{\"user_id\":399,\"movie_id\":85,\"rating\":2.5,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2002-02-02T08:24:48Z\"}\n{\"user_id\":202,\"movie_id\":4,\"rating\":3.1,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2000-07-25T02:05:35Z\"}\n{\"user_id\":386,\"movie_id\":90,\"rating\":4.6,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1996-09-18T20:12:29Z\"}\n{\"user_id\":630,\"movie_id\":63,\"rating\":2.0,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2008-11-18T20:21:46Z\"}\n{\"user_id\":855,\"movie_id\":89,\"rating\":2.3,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2002-05-05T14:15:16Z\"}\n{\"user_id\":33,\"movie_id\":34,\"rating\":4.6,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2005-08-12T00:51:32Z\"}\n{\"user_id\":418,\"movie_id\":74,\"rating\":4.4,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1992-11-08T21:17:16Z\"}\n{\"user_id\":337,\"movie_id\":97,\"rating\":2.5,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"1996-07-16T22:21:55Z\"}\n{\"user_id\":925,\"movie_id\":98,\"rating\":4.5,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"1995-12-15T08:48:04Z\"}\n{\"user_id\":224,\"movie_id\":87,\"rating\":2.0,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1998-09-06T10:54:45Z\"}\n{\"user_id\":837,\"movie_id\":46,\"rating\":4.0,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"2003-10-05T11:09:55Z\"}\n{\"user_id\":326,\"movie_id\":93,\"rating\":3.0,\"review\":\"Duis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1991-02-02T16:11:18Z\"}\n{\"user_id\":834,\"movie_id\":3,\"rating\":4.3,\"review\":\"Proin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\\n\\nInteger ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1991-12-13T23:53:25Z\"}\n{\"user_id\":547,\"movie_id\":64,\"rating\":3.7,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1998-06-26T04:42:28Z\"}\n{\"user_id\":15,\"movie_id\":99,\"rating\":3.9,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2008-09-17T10:10:48Z\"}\n{\"user_id\":871,\"movie_id\":13,\"rating\":4.8,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1993-11-06T14:24:14Z\"}\n{\"user_id\":838,\"movie_id\":99,\"rating\":4.2,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2006-04-11T22:01:57Z\"}\n{\"user_id\":108,\"movie_id\":84,\"rating\":2.6,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1997-01-11T09:22:46Z\"}\n{\"user_id\":598,\"movie_id\":93,\"rating\":4.4,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"1994-03-30T03:28:59Z\"}\n{\"user_id\":679,\"movie_id\":3,\"rating\":2.0,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"2007-12-22T11:47:02Z\"}\n{\"user_id\":469,\"movie_id\":90,\"rating\":2.2,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1997-05-20T16:44:10Z\"}\n{\"user_id\":346,\"movie_id\":47,\"rating\":1.7,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2009-02-23T02:36:12Z\"}\n{\"user_id\":481,\"movie_id\":71,\"rating\":2.4,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1993-09-18T21:23:31Z\"}\n{\"user_id\":426,\"movie_id\":75,\"rating\":3.0,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1997-12-29T09:05:54Z\"}\n{\"user_id\":655,\"movie_id\":49,\"rating\":3.0,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2008-03-09T20:15:49Z\"}\n{\"user_id\":133,\"movie_id\":36,\"rating\":3.1,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2000-07-21T05:44:47Z\"}\n{\"user_id\":220,\"movie_id\":80,\"rating\":2.8,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1993-02-12T22:14:42Z\"}\n{\"user_id\":429,\"movie_id\":30,\"rating\":1.9,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2008-06-20T02:21:14Z\"}\n{\"user_id\":656,\"movie_id\":79,\"rating\":3.2,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2003-03-17T23:15:10Z\"}\n{\"user_id\":577,\"movie_id\":32,\"rating\":1.8,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"1996-04-18T17:17:55Z\"}\n{\"user_id\":338,\"movie_id\":48,\"rating\":1.7,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2008-01-04T07:46:25Z\"}\n{\"user_id\":450,\"movie_id\":18,\"rating\":1.2,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"2008-12-25T07:13:05Z\"}\n{\"user_id\":457,\"movie_id\":49,\"rating\":3.7,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1996-07-01T21:54:18Z\"}\n{\"user_id\":931,\"movie_id\":28,\"rating\":1.6,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"2004-11-22T16:17:42Z\"}\n{\"user_id\":331,\"movie_id\":30,\"rating\":3.8,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2009-06-13T15:26:07Z\"}\n{\"user_id\":656,\"movie_id\":23,\"rating\":3.5,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2004-04-20T07:04:12Z\"}\n{\"user_id\":607,\"movie_id\":36,\"rating\":1.8,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2002-09-24T08:58:47Z\"}\n{\"user_id\":764,\"movie_id\":69,\"rating\":4.3,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1998-01-10T00:48:48Z\"}\n{\"user_id\":250,\"movie_id\":37,\"rating\":2.8,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2008-08-06T15:50:49Z\"}\n{\"user_id\":165,\"movie_id\":37,\"rating\":1.0,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2000-01-23T14:45:12Z\"}\n{\"user_id\":680,\"movie_id\":48,\"rating\":1.4,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1990-12-14T03:52:17Z\"}\n{\"user_id\":967,\"movie_id\":15,\"rating\":4.7,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"1992-05-27T09:00:20Z\"}\n{\"user_id\":756,\"movie_id\":3,\"rating\":4.9,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2003-05-30T09:21:52Z\"}\n{\"user_id\":334,\"movie_id\":79,\"rating\":2.2,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1998-06-26T09:38:07Z\"}\n{\"user_id\":215,\"movie_id\":95,\"rating\":1.0,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1998-06-15T02:53:17Z\"}\n{\"user_id\":708,\"movie_id\":84,\"rating\":3.7,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1997-07-26T13:07:48Z\"}\n{\"user_id\":654,\"movie_id\":1,\"rating\":1.2,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1990-11-12T06:25:19Z\"}\n{\"user_id\":694,\"movie_id\":54,\"rating\":1.7,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2007-12-26T12:20:01Z\"}\n{\"user_id\":195,\"movie_id\":25,\"rating\":2.9,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"1990-10-28T10:47:06Z\"}\n{\"user_id\":966,\"movie_id\":56,\"rating\":4.4,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2006-03-17T07:47:09Z\"}\n{\"user_id\":19,\"movie_id\":62,\"rating\":3.1,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2002-07-27T12:24:46Z\"}\n{\"user_id\":713,\"movie_id\":13,\"rating\":3.3,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2006-01-02T03:50:23Z\"}\n{\"user_id\":512,\"movie_id\":79,\"rating\":1.4,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1992-10-01T17:19:55Z\"}\n{\"user_id\":630,\"movie_id\":17,\"rating\":1.7,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1991-09-21T19:57:05Z\"}\n{\"user_id\":971,\"movie_id\":58,\"rating\":4.5,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2001-10-07T04:22:58Z\"}\n{\"user_id\":441,\"movie_id\":58,\"rating\":3.4,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2000-03-29T13:42:22Z\"}\n{\"user_id\":360,\"movie_id\":83,\"rating\":3.5,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2008-07-15T19:11:45Z\"}\n{\"user_id\":343,\"movie_id\":92,\"rating\":3.5,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1996-09-03T10:12:01Z\"}\n{\"user_id\":502,\"movie_id\":41,\"rating\":2.2,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1994-10-23T14:40:33Z\"}\n{\"user_id\":655,\"movie_id\":75,\"rating\":4.3,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1993-05-15T21:37:03Z\"}\n{\"user_id\":683,\"movie_id\":48,\"rating\":2.9,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"2006-09-17T01:21:16Z\"}\n{\"user_id\":779,\"movie_id\":4,\"rating\":3.9,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1992-07-09T23:02:31Z\"}\n{\"user_id\":820,\"movie_id\":68,\"rating\":1.8,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1995-06-06T12:26:40Z\"}\n{\"user_id\":216,\"movie_id\":48,\"rating\":2.4,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1999-03-23T03:04:27Z\"}\n{\"user_id\":327,\"movie_id\":6,\"rating\":1.4,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"2001-07-16T03:12:51Z\"}\n{\"user_id\":529,\"movie_id\":20,\"rating\":2.4,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\",\"created\":\"2006-02-22T06:11:09Z\"}\n{\"user_id\":318,\"movie_id\":47,\"rating\":4.5,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1994-02-11T14:41:21Z\"}\n{\"user_id\":966,\"movie_id\":39,\"rating\":4.2,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\",\"created\":\"2006-10-10T23:35:55Z\"}\n{\"user_id\":880,\"movie_id\":15,\"rating\":1.8,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1999-04-09T20:00:45Z\"}\n{\"user_id\":732,\"movie_id\":83,\"rating\":1.0,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1999-09-10T00:14:36Z\"}\n{\"user_id\":997,\"movie_id\":80,\"rating\":1.6,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1992-11-21T08:12:33Z\"}\n{\"user_id\":529,\"movie_id\":33,\"rating\":1.4,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1991-05-17T02:53:07Z\"}\n{\"user_id\":189,\"movie_id\":70,\"rating\":2.5,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2009-09-30T16:59:38Z\"}\n{\"user_id\":480,\"movie_id\":11,\"rating\":2.5,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1990-11-19T22:57:38Z\"}\n{\"user_id\":295,\"movie_id\":12,\"rating\":3.0,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"2000-05-14T21:24:45Z\"}\n{\"user_id\":423,\"movie_id\":65,\"rating\":2.5,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1990-10-04T01:44:26Z\"}\n{\"user_id\":319,\"movie_id\":22,\"rating\":4.6,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1990-10-29T05:43:20Z\"}\n{\"user_id\":282,\"movie_id\":84,\"rating\":2.7,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1999-03-17T12:12:46Z\"}\n{\"user_id\":177,\"movie_id\":63,\"rating\":2.3,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2007-06-02T05:31:52Z\"}\n{\"user_id\":316,\"movie_id\":86,\"rating\":2.7,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2008-04-20T22:26:19Z\"}\n{\"user_id\":731,\"movie_id\":63,\"rating\":1.7,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"1994-12-10T07:53:14Z\"}\n{\"user_id\":909,\"movie_id\":78,\"rating\":2.4,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"1991-02-12T12:51:24Z\"}\n{\"user_id\":699,\"movie_id\":99,\"rating\":4.1,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2001-04-01T22:40:30Z\"}\n{\"user_id\":422,\"movie_id\":5,\"rating\":2.7,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1992-01-14T17:49:58Z\"}\n{\"user_id\":925,\"movie_id\":57,\"rating\":2.8,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1990-01-17T11:41:51Z\"}\n{\"user_id\":229,\"movie_id\":31,\"rating\":4.0,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"1993-03-27T10:45:05Z\"}\n{\"user_id\":333,\"movie_id\":93,\"rating\":3.1,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"1991-12-15T03:39:24Z\"}\n{\"user_id\":653,\"movie_id\":53,\"rating\":3.6,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2008-04-03T04:32:06Z\"}\n{\"user_id\":848,\"movie_id\":50,\"rating\":4.5,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"1996-01-29T20:56:47Z\"}\n{\"user_id\":831,\"movie_id\":72,\"rating\":2.6,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2005-10-06T11:32:01Z\"}\n{\"user_id\":438,\"movie_id\":31,\"rating\":4.6,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"2007-09-24T12:23:23Z\"}\n{\"user_id\":141,\"movie_id\":12,\"rating\":1.7,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2002-10-12T23:47:29Z\"}\n{\"user_id\":480,\"movie_id\":73,\"rating\":5.0,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"1993-03-03T17:48:20Z\"}\n{\"user_id\":136,\"movie_id\":58,\"rating\":1.8,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2001-02-10T14:03:00Z\"}\n{\"user_id\":518,\"movie_id\":5,\"rating\":4.2,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1997-02-27T21:40:16Z\"}\n{\"user_id\":154,\"movie_id\":20,\"rating\":2.1,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1995-12-05T21:58:23Z\"}\n{\"user_id\":885,\"movie_id\":32,\"rating\":3.2,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2005-09-13T21:13:52Z\"}\n{\"user_id\":53,\"movie_id\":63,\"rating\":3.9,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1998-09-06T05:21:50Z\"}\n{\"user_id\":940,\"movie_id\":66,\"rating\":2.3,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\",\"created\":\"2001-10-07T17:53:16Z\"}\n{\"user_id\":987,\"movie_id\":43,\"rating\":1.9,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"2000-10-19T08:26:10Z\"}\n{\"user_id\":849,\"movie_id\":48,\"rating\":3.4,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2008-08-23T17:51:34Z\"}\n{\"user_id\":871,\"movie_id\":52,\"rating\":2.4,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"1993-07-10T17:49:00Z\"}\n{\"user_id\":644,\"movie_id\":45,\"rating\":4.3,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2006-11-05T02:38:18Z\"}\n{\"user_id\":677,\"movie_id\":88,\"rating\":2.9,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"2003-03-01T20:16:00Z\"}\n{\"user_id\":286,\"movie_id\":59,\"rating\":4.1,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2005-09-30T23:51:31Z\"}\n{\"user_id\":969,\"movie_id\":33,\"rating\":4.3,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1991-09-15T20:36:43Z\"}\n{\"user_id\":439,\"movie_id\":49,\"rating\":1.5,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"1996-04-12T04:47:20Z\"}\n{\"user_id\":831,\"movie_id\":78,\"rating\":4.6,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2005-10-17T06:58:41Z\"}\n{\"user_id\":333,\"movie_id\":20,\"rating\":3.2,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2010-01-05T01:24:55Z\"}\n{\"user_id\":627,\"movie_id\":72,\"rating\":3.6,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\\n\\nCum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"2008-11-19T10:04:27Z\"}\n{\"user_id\":678,\"movie_id\":57,\"rating\":4.9,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"2008-02-19T18:08:37Z\"}\n{\"user_id\":818,\"movie_id\":79,\"rating\":3.7,\"review\":\"Praesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\\n\\nMorbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\\n\\nFusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2008-06-07T21:24:51Z\"}\n{\"user_id\":734,\"movie_id\":70,\"rating\":4.2,\"review\":\"Curabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2007-07-04T10:32:34Z\"}\n{\"user_id\":265,\"movie_id\":5,\"rating\":3.5,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1993-06-19T08:59:16Z\"}\n{\"user_id\":134,\"movie_id\":91,\"rating\":1.3,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2002-06-15T04:18:04Z\"}\n{\"user_id\":26,\"movie_id\":59,\"rating\":3.6,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1993-11-02T22:32:58Z\"}\n{\"user_id\":185,\"movie_id\":39,\"rating\":2.2,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2009-01-04T05:23:17Z\"}\n{\"user_id\":651,\"movie_id\":44,\"rating\":1.0,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1990-08-02T18:38:34Z\"}\n{\"user_id\":377,\"movie_id\":86,\"rating\":4.4,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1992-08-07T10:36:38Z\"}\n{\"user_id\":343,\"movie_id\":45,\"rating\":2.5,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1991-11-07T12:01:21Z\"}\n{\"user_id\":27,\"movie_id\":26,\"rating\":1.4,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"2003-05-14T22:13:47Z\"}\n{\"user_id\":764,\"movie_id\":39,\"rating\":1.1,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"2002-05-17T10:03:11Z\"}\n{\"user_id\":219,\"movie_id\":48,\"rating\":3.3,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1991-06-25T17:34:19Z\"}\n{\"user_id\":63,\"movie_id\":82,\"rating\":3.2,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"1993-08-28T15:59:18Z\"}\n{\"user_id\":277,\"movie_id\":20,\"rating\":4.6,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1995-07-15T13:19:05Z\"}\n{\"user_id\":864,\"movie_id\":64,\"rating\":3.8,\"review\":\"Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2000-05-09T12:39:41Z\"}\n{\"user_id\":119,\"movie_id\":87,\"rating\":4.6,\"review\":\"Praesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2008-12-03T21:49:27Z\"}\n{\"user_id\":49,\"movie_id\":9,\"rating\":3.1,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2009-09-22T11:33:42Z\"}\n{\"user_id\":146,\"movie_id\":89,\"rating\":3.1,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\",\"created\":\"1997-10-28T14:28:53Z\"}\n{\"user_id\":950,\"movie_id\":3,\"rating\":1.9,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2006-05-26T03:36:36Z\"}\n{\"user_id\":47,\"movie_id\":84,\"rating\":2.4,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2002-09-19T15:04:04Z\"}\n{\"user_id\":561,\"movie_id\":7,\"rating\":3.5,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2002-06-26T21:07:40Z\"}\n{\"user_id\":873,\"movie_id\":44,\"rating\":3.5,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"1993-08-26T16:51:05Z\"}\n{\"user_id\":39,\"movie_id\":44,\"rating\":4.7,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"2005-04-29T01:36:27Z\"}\n{\"user_id\":563,\"movie_id\":74,\"rating\":3.4,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2002-11-17T00:35:24Z\"}\n{\"user_id\":209,\"movie_id\":67,\"rating\":1.9,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\",\"created\":\"2000-05-25T20:31:28Z\"}\n{\"user_id\":416,\"movie_id\":27,\"rating\":2.3,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1998-05-29T04:24:38Z\"}\n{\"user_id\":581,\"movie_id\":95,\"rating\":3.4,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\",\"created\":\"1997-07-29T07:05:20Z\"}\n{\"user_id\":864,\"movie_id\":90,\"rating\":3.6,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\\n\\nCurabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1990-09-08T19:22:43Z\"}\n{\"user_id\":555,\"movie_id\":84,\"rating\":1.5,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1999-10-14T11:26:40Z\"}\n{\"user_id\":718,\"movie_id\":69,\"rating\":2.4,\"review\":\"Nullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"1992-10-18T04:10:22Z\"}\n{\"user_id\":913,\"movie_id\":44,\"rating\":4.1,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"1993-08-17T14:17:35Z\"}\n{\"user_id\":736,\"movie_id\":51,\"rating\":4.7,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2004-01-15T15:15:25Z\"}\n{\"user_id\":738,\"movie_id\":87,\"rating\":3.1,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2002-04-21T12:07:13Z\"}\n{\"user_id\":607,\"movie_id\":57,\"rating\":3.3,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2001-04-16T02:43:01Z\"}\n{\"user_id\":335,\"movie_id\":92,\"rating\":1.6,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2001-08-19T19:15:47Z\"}\n{\"user_id\":258,\"movie_id\":11,\"rating\":2.8,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1991-06-23T22:21:14Z\"}\n{\"user_id\":815,\"movie_id\":85,\"rating\":4.1,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2004-10-02T21:21:51Z\"}\n{\"user_id\":413,\"movie_id\":33,\"rating\":4.7,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2007-01-19T15:42:13Z\"}\n{\"user_id\":449,\"movie_id\":65,\"rating\":4.6,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1995-05-09T07:41:14Z\"}\n{\"user_id\":607,\"movie_id\":27,\"rating\":2.6,\"review\":\"Proin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\",\"created\":\"2007-12-07T08:46:15Z\"}\n{\"user_id\":324,\"movie_id\":88,\"rating\":1.5,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1995-12-06T04:07:42Z\"}\n{\"user_id\":164,\"movie_id\":30,\"rating\":1.8,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1998-09-28T22:19:00Z\"}\n{\"user_id\":664,\"movie_id\":37,\"rating\":3.0,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\\n\\nDuis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"2009-03-17T04:53:12Z\"}\n{\"user_id\":494,\"movie_id\":12,\"rating\":1.1,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2000-11-22T20:26:23Z\"}\n{\"user_id\":824,\"movie_id\":36,\"rating\":1.5,\"review\":\"Aliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2003-03-23T23:27:41Z\"}\n{\"user_id\":10,\"movie_id\":22,\"rating\":3.1,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"1997-04-23T10:19:15Z\"}\n{\"user_id\":842,\"movie_id\":67,\"rating\":4.7,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1996-09-07T23:39:24Z\"}\n{\"user_id\":500,\"movie_id\":85,\"rating\":2.1,\"review\":\"Integer tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\\n\\nPraesent blandit. Nam nulla. Integer pede justo, lacinia eget, tincidunt eget, tempus vel, pede.\",\"created\":\"1996-10-28T07:02:15Z\"}\n{\"user_id\":653,\"movie_id\":34,\"rating\":4.5,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"1996-04-19T01:57:06Z\"}\n{\"user_id\":917,\"movie_id\":6,\"rating\":2.0,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"1997-10-13T06:56:47Z\"}\n{\"user_id\":453,\"movie_id\":34,\"rating\":3.4,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1998-11-21T02:07:07Z\"}\n{\"user_id\":74,\"movie_id\":50,\"rating\":3.3,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2003-11-09T22:32:05Z\"}\n{\"user_id\":228,\"movie_id\":42,\"rating\":1.9,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2001-06-24T11:14:38Z\"}\n{\"user_id\":126,\"movie_id\":47,\"rating\":3.2,\"review\":\"Vestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2009-05-13T03:49:57Z\"}\n{\"user_id\":985,\"movie_id\":68,\"rating\":3.8,\"review\":\"Morbi porttitor lorem id ligula. Suspendisse ornare consequat lectus. In est risus, auctor sed, tristique in, tempus sit amet, sem.\",\"created\":\"2000-03-19T09:14:49Z\"}\n{\"user_id\":442,\"movie_id\":3,\"rating\":1.2,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2005-03-11T05:29:03Z\"}\n{\"user_id\":644,\"movie_id\":34,\"rating\":3.7,\"review\":\"In congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"1992-06-07T05:14:32Z\"}\n{\"user_id\":804,\"movie_id\":17,\"rating\":2.6,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2004-06-05T05:50:23Z\"}\n{\"user_id\":793,\"movie_id\":65,\"rating\":3.9,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\\n\\nCurabitur at ipsum ac tellus semper interdum. Mauris ullamcorper purus sit amet nulla. Quisque arcu libero, rutrum ac, lobortis vel, dapibus at, diam.\",\"created\":\"2006-04-06T23:30:06Z\"}\n{\"user_id\":720,\"movie_id\":65,\"rating\":4.6,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1993-07-09T21:15:22Z\"}\n{\"user_id\":113,\"movie_id\":27,\"rating\":3.1,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2009-04-18T16:13:42Z\"}\n{\"user_id\":697,\"movie_id\":85,\"rating\":2.8,\"review\":\"Nullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\\n\\nMorbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\",\"created\":\"2008-07-09T00:39:53Z\"}\n{\"user_id\":31,\"movie_id\":28,\"rating\":1.8,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\\n\\nIn quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\",\"created\":\"2004-05-31T09:54:38Z\"}\n{\"user_id\":217,\"movie_id\":48,\"rating\":2.3,\"review\":\"Cras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\\n\\nPhasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2004-12-02T17:43:11Z\"}\n{\"user_id\":278,\"movie_id\":45,\"rating\":3.5,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\\n\\nCras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\",\"created\":\"1999-03-10T02:48:53Z\"}\n{\"user_id\":814,\"movie_id\":21,\"rating\":1.6,\"review\":\"Nulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\\n\\nQuisque porta volutpat erat. Quisque erat eros, viverra eget, congue eget, semper rutrum, nulla. Nunc purus.\",\"created\":\"1998-04-04T22:46:24Z\"}\n{\"user_id\":535,\"movie_id\":91,\"rating\":2.6,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"1992-06-04T11:09:24Z\"}\n{\"user_id\":232,\"movie_id\":78,\"rating\":4.7,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2009-09-16T23:45:45Z\"}\n{\"user_id\":934,\"movie_id\":59,\"rating\":3.1,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"1993-12-09T03:11:03Z\"}\n{\"user_id\":762,\"movie_id\":60,\"rating\":3.5,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\\n\\nCurabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"2004-06-15T00:42:17Z\"}\n{\"user_id\":86,\"movie_id\":14,\"rating\":2.0,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1998-12-01T21:23:27Z\"}\n{\"user_id\":700,\"movie_id\":4,\"rating\":3.6,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1997-09-06T22:47:41Z\"}\n{\"user_id\":451,\"movie_id\":4,\"rating\":3.5,\"review\":\"Morbi non lectus. Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis.\\n\\nFusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"2003-06-05T19:10:34Z\"}\n{\"user_id\":38,\"movie_id\":76,\"rating\":1.1,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1998-04-29T18:45:05Z\"}\n{\"user_id\":852,\"movie_id\":2,\"rating\":3.8,\"review\":\"Suspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\",\"created\":\"1999-01-11T23:41:57Z\"}\n{\"user_id\":295,\"movie_id\":26,\"rating\":3.7,\"review\":\"Vestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\\n\\nIn hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\",\"created\":\"2007-01-21T11:55:52Z\"}\n{\"user_id\":185,\"movie_id\":64,\"rating\":4.1,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2000-04-03T21:05:50Z\"}\n{\"user_id\":53,\"movie_id\":65,\"rating\":2.4,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2000-06-25T11:58:53Z\"}\n{\"user_id\":941,\"movie_id\":68,\"rating\":3.8,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"1994-11-20T19:03:12Z\"}\n{\"user_id\":958,\"movie_id\":17,\"rating\":3.3,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\\n\\nCras non velit nec nisi vulputate nonummy. Maecenas tincidunt lacus at velit. Vivamus vel nulla eget eros elementum pellentesque.\",\"created\":\"2006-01-02T06:59:54Z\"}\n{\"user_id\":426,\"movie_id\":62,\"rating\":2.9,\"review\":\"Cras mi pede, malesuada in, imperdiet et, commodo vulputate, justo. In blandit ultrices enim. Lorem ipsum dolor sit amet, consectetuer adipiscing elit.\\n\\nProin interdum mauris non ligula pellentesque ultrices. Phasellus id sapien in sapien iaculis congue. Vivamus metus arcu, adipiscing molestie, hendrerit at, vulputate vitae, nisl.\\n\\nAenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1990-09-10T15:39:56Z\"}\n{\"user_id\":980,\"movie_id\":26,\"rating\":1.7,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"2003-11-15T15:33:06Z\"}\n{\"user_id\":541,\"movie_id\":77,\"rating\":3.6,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\",\"created\":\"1994-02-14T07:19:08Z\"}\n{\"user_id\":899,\"movie_id\":40,\"rating\":1.3,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\",\"created\":\"2005-01-12T14:26:12Z\"}\n{\"user_id\":358,\"movie_id\":22,\"rating\":4.9,\"review\":\"In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus.\\n\\nNulla ut erat id mauris vulputate elementum. Nullam varius. Nulla facilisi.\",\"created\":\"1991-12-10T19:38:19Z\"}\n{\"user_id\":939,\"movie_id\":37,\"rating\":4.7,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1991-05-14T19:20:03Z\"}\n{\"user_id\":56,\"movie_id\":61,\"rating\":2.6,\"review\":\"Sed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\",\"created\":\"1997-12-31T06:36:44Z\"}\n{\"user_id\":347,\"movie_id\":67,\"rating\":4.4,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2001-07-17T05:39:12Z\"}\n{\"user_id\":540,\"movie_id\":37,\"rating\":2.0,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\\n\\nEtiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\\n\\nPraesent id massa id nisl venenatis lacinia. Aenean sit amet justo. Morbi ut odio.\",\"created\":\"2004-01-28T19:37:01Z\"}\n{\"user_id\":350,\"movie_id\":95,\"rating\":3.4,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\\n\\nProin leo odio, porttitor id, consequat in, consequat ut, nulla. Sed accumsan felis. Ut at dolor quis odio consequat varius.\",\"created\":\"1991-03-21T04:05:17Z\"}\n{\"user_id\":878,\"movie_id\":13,\"rating\":1.9,\"review\":\"Maecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2007-10-26T09:44:52Z\"}\n{\"user_id\":935,\"movie_id\":15,\"rating\":4.1,\"review\":\"Curabitur gravida nisi at nibh. In hac habitasse platea dictumst. Aliquam augue quam, sollicitudin vitae, consectetuer eget, rutrum at, lorem.\\n\\nInteger tincidunt ante vel ipsum. Praesent blandit lacinia erat. Vestibulum sed magna at nunc commodo placerat.\",\"created\":\"1990-06-30T09:55:37Z\"}\n{\"user_id\":890,\"movie_id\":29,\"rating\":2.4,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2000-09-18T15:26:09Z\"}\n{\"user_id\":771,\"movie_id\":97,\"rating\":1.2,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\",\"created\":\"2005-10-12T10:57:06Z\"}\n{\"user_id\":479,\"movie_id\":36,\"rating\":1.9,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1997-06-10T09:33:18Z\"}\n{\"user_id\":310,\"movie_id\":6,\"rating\":4.2,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\",\"created\":\"2008-03-19T03:43:25Z\"}\n{\"user_id\":97,\"movie_id\":24,\"rating\":1.4,\"review\":\"Sed ante. Vivamus tortor. Duis mattis egestas metus.\\n\\nAenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.\\n\\nQuisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\",\"created\":\"1998-01-28T03:00:10Z\"}\n{\"user_id\":511,\"movie_id\":51,\"rating\":2.0,\"review\":\"Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus vestibulum sagittis sapien. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.\",\"created\":\"1999-04-16T08:18:04Z\"}\n{\"user_id\":874,\"movie_id\":38,\"rating\":2.1,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\",\"created\":\"1999-07-14T02:48:56Z\"}\n{\"user_id\":998,\"movie_id\":20,\"rating\":1.4,\"review\":\"Mauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\",\"created\":\"1993-04-22T12:52:58Z\"}\n{\"user_id\":141,\"movie_id\":48,\"rating\":2.6,\"review\":\"Pellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"1993-03-14T14:52:18Z\"}\n{\"user_id\":926,\"movie_id\":8,\"rating\":4.0,\"review\":\"Proin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\\n\\nDuis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"2005-07-18T10:28:55Z\"}\n{\"user_id\":575,\"movie_id\":16,\"rating\":3.6,\"review\":\"In sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\\n\\nSuspendisse potenti. In eleifend quam a odio. In hac habitasse platea dictumst.\\n\\nMaecenas ut massa quis augue luctus tincidunt. Nulla mollis molestie lorem. Quisque ut erat.\",\"created\":\"2000-09-27T17:38:10Z\"}\n{\"user_id\":960,\"movie_id\":25,\"rating\":4.1,\"review\":\"Maecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"2001-09-14T00:16:23Z\"}\n{\"user_id\":185,\"movie_id\":12,\"rating\":2.6,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\",\"created\":\"2000-08-22T20:50:16Z\"}\n{\"user_id\":309,\"movie_id\":1,\"rating\":2.0,\"review\":\"Nam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"1996-02-11T18:12:01Z\"}\n{\"user_id\":790,\"movie_id\":93,\"rating\":2.3,\"review\":\"In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\\n\\nAliquam quis turpis eget elit sodales scelerisque. Mauris sit amet eros. Suspendisse accumsan tortor quis turpis.\\n\\nSed ante. Vivamus tortor. Duis mattis egestas metus.\",\"created\":\"2005-03-05T10:21:50Z\"}\n{\"user_id\":434,\"movie_id\":87,\"rating\":3.6,\"review\":\"Duis aliquam convallis nunc. Proin at turpis a pede posuere nonummy. Integer non velit.\\n\\nDonec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\",\"created\":\"1998-02-17T12:44:36Z\"}\n{\"user_id\":316,\"movie_id\":43,\"rating\":2.2,\"review\":\"Quisque id justo sit amet sapien dignissim vestibulum. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla dapibus dolor vel est. Donec odio justo, sollicitudin ut, suscipit a, feugiat et, eros.\\n\\nVestibulum ac est lacinia nisi venenatis tristique. Fusce congue, diam id ornare imperdiet, sapien urna pretium nisl, ut volutpat sapien arcu sed augue. Aliquam erat volutpat.\\n\\nIn congue. Etiam justo. Etiam pretium iaculis justo.\",\"created\":\"2000-02-11T09:11:42Z\"}\n{\"user_id\":608,\"movie_id\":20,\"rating\":1.2,\"review\":\"Aenean lectus. Pellentesque eget nunc. Donec quis orci eget orci vehicula condimentum.\",\"created\":\"1994-08-29T19:04:47Z\"}\n{\"user_id\":938,\"movie_id\":16,\"rating\":3.6,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\",\"created\":\"1994-06-10T03:28:52Z\"}\n{\"user_id\":652,\"movie_id\":47,\"rating\":2.3,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2002-01-13T23:53:51Z\"}\n{\"user_id\":450,\"movie_id\":1,\"rating\":1.4,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1991-10-05T02:50:49Z\"}\n{\"user_id\":838,\"movie_id\":70,\"rating\":1.8,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\\n\\nIn sagittis dui vel nisl. Duis ac nibh. Fusce lacus purus, aliquet at, feugiat non, pretium quis, lectus.\",\"created\":\"1993-10-19T06:06:52Z\"}\n{\"user_id\":871,\"movie_id\":93,\"rating\":3.2,\"review\":\"Duis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"2006-10-26T00:59:19Z\"}\n{\"user_id\":32,\"movie_id\":12,\"rating\":2.7,\"review\":\"In quis justo. Maecenas rhoncus aliquam lacus. Morbi quis tortor id nulla ultrices aliquet.\\n\\nMaecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\",\"created\":\"2002-09-03T04:22:28Z\"}\n{\"user_id\":653,\"movie_id\":30,\"rating\":1.8,\"review\":\"Integer ac leo. Pellentesque ultrices mattis odio. Donec vitae nisi.\\n\\nNam ultrices, libero non mattis pulvinar, nulla pede ullamcorper augue, a suscipit nulla elit ac nulla. Sed vel enim sit amet nunc viverra dapibus. Nulla suscipit ligula in lacus.\",\"created\":\"2009-10-13T12:57:22Z\"}\n{\"user_id\":817,\"movie_id\":12,\"rating\":4.8,\"review\":\"Curabitur in libero ut massa volutpat convallis. Morbi odio odio, elementum eu, interdum eu, tincidunt in, leo. Maecenas pulvinar lobortis est.\\n\\nPhasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"1993-06-25T11:39:53Z\"}\n{\"user_id\":799,\"movie_id\":54,\"rating\":2.6,\"review\":\"Fusce consequat. Nulla nisl. Nunc nisl.\\n\\nDuis bibendum, felis sed interdum venenatis, turpis enim blandit mi, in porttitor pede justo eu massa. Donec dapibus. Duis at velit eu est congue elementum.\\n\\nIn hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.\",\"created\":\"2005-04-02T03:22:40Z\"}\n{\"user_id\":913,\"movie_id\":90,\"rating\":3.8,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\\n\\nSed sagittis. Nam congue, risus semper porta volutpat, quam pede lobortis ligula, sit amet eleifend pede libero quis orci. Nullam molestie nibh in lectus.\\n\\nPellentesque at nulla. Suspendisse potenti. Cras in purus eu magna vulputate luctus.\",\"created\":\"2005-08-12T01:54:28Z\"}\n{\"user_id\":809,\"movie_id\":63,\"rating\":4.9,\"review\":\"Donec diam neque, vestibulum eget, vulputate ut, ultrices vel, augue. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec pharetra, magna vestibulum aliquet ultrices, erat tortor sollicitudin mi, sit amet lobortis sapien sapien non mi. Integer ac neque.\\n\\nDuis bibendum. Morbi non quam nec dui luctus rutrum. Nulla tellus.\",\"created\":\"1993-07-18T04:38:43Z\"}\n{\"user_id\":801,\"movie_id\":52,\"rating\":1.2,\"review\":\"Duis consequat dui nec nisi volutpat eleifend. Donec ut dolor. Morbi vel lectus in quam fringilla rhoncus.\\n\\nMauris enim leo, rhoncus sed, vestibulum sit amet, cursus id, turpis. Integer aliquet, massa id lobortis convallis, tortor risus dapibus augue, vel accumsan tellus nisi eu orci. Mauris lacinia sapien quis libero.\\n\\nNullam sit amet turpis elementum ligula vehicula consequat. Morbi a ipsum. Integer a nibh.\",\"created\":\"1993-12-17T13:18:06Z\"}\n{\"user_id\":132,\"movie_id\":8,\"rating\":2.9,\"review\":\"Phasellus sit amet erat. Nulla tempus. Vivamus in felis eu sapien cursus vestibulum.\\n\\nProin eu mi. Nulla ac enim. In tempor, turpis nec euismod scelerisque, quam turpis adipiscing lorem, vitae mattis nibh ligula nec sem.\",\"created\":\"2009-09-10T11:33:57Z\"}\n{\"user_id\":349,\"movie_id\":72,\"rating\":1.8,\"review\":\"Phasellus in felis. Donec semper sapien a libero. Nam dui.\",\"created\":\"2005-11-26T15:16:39Z\"}\n{\"user_id\":815,\"movie_id\":67,\"rating\":4.3,\"review\":\"Maecenas leo odio, condimentum id, luctus nec, molestie sed, justo. Pellentesque viverra pede ac diam. Cras pellentesque volutpat dui.\\n\\nMaecenas tristique, est et tempus semper, est quam pharetra magna, ac consequat metus sapien ut nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris viverra diam vitae quam. Suspendisse potenti.\\n\\nNullam porttitor lacus at turpis. Donec posuere metus vitae ipsum. Aliquam non mauris.\",\"created\":\"1997-03-11T04:22:25Z\"}\n{\"user_id\":321,\"movie_id\":75,\"rating\":2.3,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"1990-10-14T10:57:34Z\"}\n{\"user_id\":831,\"movie_id\":71,\"rating\":2.8,\"review\":\"Etiam vel augue. Vestibulum rutrum rutrum neque. Aenean auctor gravida sem.\",\"created\":\"2004-09-14T11:27:07Z\"}\n{\"user_id\":952,\"movie_id\":60,\"rating\":2.9,\"review\":\"Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.\",\"created\":\"1997-03-02T05:09:27Z\"}\n{\"user_id\":326,\"movie_id\":1,\"rating\":3.0,\"review\":\"Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Proin risus. Praesent lectus.\\n\\nVestibulum quam sapien, varius ut, blandit non, interdum in, ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Duis faucibus accumsan odio. Curabitur convallis.\",\"created\":\"2007-11-10T19:21:18Z\"}"
  },
  {
    "path": "src/test/resources/data/users.json",
    "content": "{\"id\":1,\"first_name\":\"Di\",\"last_name\":\"Gother\",\"email\":\"dgother0@adobe.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":43,\"birthday\":\"1994-03-26\"}\n{\"id\":2,\"first_name\":\"Cloris\",\"last_name\":\"Willsmore\",\"email\":\"cwillsmore1@pcworld.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":10,\"birthday\":\"1995-06-18\"}\n{\"id\":3,\"first_name\":\"Calv\",\"last_name\":\"Sherwill\",\"email\":\"csherwill2@opera.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":30,\"birthday\":\"1996-11-29\"}\n{\"id\":4,\"first_name\":\"Donielle\",\"last_name\":\"Chable\",\"email\":\"dchable3@nba.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":70,\"birthday\":\"1995-05-23\"}\n{\"id\":5,\"first_name\":\"Arlen\",\"last_name\":\"Clissett\",\"email\":\"aclissett4@sakura.ne.jp\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":43,\"birthday\":\"1993-12-25\"}\n{\"id\":6,\"first_name\":\"Patrice\",\"last_name\":\"Rothchild\",\"email\":\"prothchild5@independent.co.uk\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":27,\"birthday\":\"2002-08-13\"}\n{\"id\":7,\"first_name\":\"Allie\",\"last_name\":\"Woffenden\",\"email\":\"awoffenden6@newyorker.com\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":73,\"birthday\":\"1996-12-03\"}\n{\"id\":8,\"first_name\":\"Magdalen\",\"last_name\":\"Choffin\",\"email\":\"mchoffin7@latimes.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":8,\"birthday\":\"1991-04-03\"}\n{\"id\":9,\"first_name\":\"Desdemona\",\"last_name\":\"MacGill\",\"email\":\"dmacgill8@arstechnica.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":52,\"birthday\":\"1999-10-07\"}\n{\"id\":10,\"first_name\":\"Giffer\",\"last_name\":\"Makeswell\",\"email\":\"gmakeswell9@wsj.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":80,\"birthday\":\"2002-10-24\"}\n{\"id\":11,\"first_name\":\"Gordy\",\"last_name\":\"Ferrick\",\"email\":\"gferricka@theatlantic.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":63,\"birthday\":\"1993-01-26\"}\n{\"id\":12,\"first_name\":\"Quintana\",\"last_name\":\"Temlett\",\"email\":\"qtemlettb@blinklist.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":58,\"birthday\":\"1999-11-11\"}\n{\"id\":13,\"first_name\":\"Michelle\",\"last_name\":\"Hannen\",\"email\":\"mhannenc@mozilla.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":24,\"birthday\":\"1998-10-23\"}\n{\"id\":14,\"first_name\":\"Hurlee\",\"last_name\":\"Girardot\",\"email\":\"hgirardotd@xinhuanet.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":77,\"birthday\":\"2000-04-25\"}\n{\"id\":15,\"first_name\":\"Gussie\",\"last_name\":\"Burberow\",\"email\":\"gburberowe@theguardian.com\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":38,\"birthday\":\"1994-10-06\"}\n{\"id\":16,\"first_name\":\"Bethanne\",\"last_name\":\"Mainson\",\"email\":\"bmainsonf@cbc.ca\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":18,\"birthday\":\"2006-04-16\"}\n{\"id\":17,\"first_name\":\"Thane\",\"last_name\":\"Bramich\",\"email\":\"tbramichg@amazon.de\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":15,\"birthday\":\"2004-02-12\"}\n{\"id\":18,\"first_name\":\"See\",\"last_name\":\"Dulin\",\"email\":\"sdulinh@addtoany.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":18,\"birthday\":\"2004-05-19\"}\n{\"id\":19,\"first_name\":\"Patricio\",\"last_name\":\"Smither\",\"email\":\"psmitheri@auda.org.au\",\"owns_house\":false,\"favorite_color\":null,\"age\":38,\"birthday\":\"1998-04-03\"}\n{\"id\":20,\"first_name\":\"Darcy\",\"last_name\":\"Hrus\",\"email\":\"dhrusj@cpanel.net\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":9,\"birthday\":\"1998-07-28\"}\n{\"id\":21,\"first_name\":\"Evan\",\"last_name\":\"Devon\",\"email\":\"edevonk@washington.edu\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":1,\"birthday\":\"1996-11-27\"}\n{\"id\":22,\"first_name\":\"Corinna\",\"last_name\":\"Margrett\",\"email\":\"cmargrettl@webnode.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":53,\"birthday\":\"1997-01-15\"}\n{\"id\":23,\"first_name\":\"Camila\",\"last_name\":\"Kennermann\",\"email\":\"ckennermannm@tinyurl.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":8,\"birthday\":\"2007-01-22\"}\n{\"id\":24,\"first_name\":\"Marmaduke\",\"last_name\":\"Goodlip\",\"email\":\"mgoodlipn@usgs.gov\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":72,\"birthday\":\"1997-01-22\"}\n{\"id\":25,\"first_name\":\"Fina\",\"last_name\":\"Josiah\",\"email\":\"fjosiaho@facebook.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":14,\"birthday\":\"2001-12-20\"}\n{\"id\":26,\"first_name\":\"Lindon\",\"last_name\":\"Bristo\",\"email\":\"lbristop@moonfruit.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":61,\"birthday\":\"1995-10-21\"}\n{\"id\":27,\"first_name\":\"Benjy\",\"last_name\":\"Cantrill\",\"email\":\"bcantrillq@wired.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":74,\"birthday\":\"2006-04-24\"}\n{\"id\":28,\"first_name\":\"Porter\",\"last_name\":\"Winley\",\"email\":\"pwinleyr@slate.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":41,\"birthday\":\"2007-10-20\"}\n{\"id\":29,\"first_name\":\"Davine\",\"last_name\":\"Canniffe\",\"email\":\"dcanniffes@independent.co.uk\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":54,\"birthday\":\"1991-02-25\"}\n{\"id\":30,\"first_name\":\"Nedi\",\"last_name\":\"Gogan\",\"email\":\"ngogant@dagondesign.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":21,\"birthday\":\"2008-02-22\"}\n{\"id\":31,\"first_name\":\"Forrest\",\"last_name\":\"Muzzall\",\"email\":\"fmuzzallu@microsoft.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":35,\"birthday\":\"1992-02-26\"}\n{\"id\":32,\"first_name\":\"Petronilla\",\"last_name\":\"Lofthouse\",\"email\":\"plofthousev@flickr.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":46,\"birthday\":\"1994-08-18\"}\n{\"id\":33,\"first_name\":\"Andriette\",\"last_name\":\"Astridge\",\"email\":\"aastridgew@ca.gov\",\"owns_house\":false,\"favorite_color\":null,\"age\":7,\"birthday\":\"1996-06-03\"}\n{\"id\":34,\"first_name\":\"Blair\",\"last_name\":\"Gabbitis\",\"email\":\"bgabbitisx@liveinternet.ru\",\"owns_house\":false,\"favorite_color\":null,\"age\":15,\"birthday\":\"1997-10-09\"}\n{\"id\":35,\"first_name\":\"David\",\"last_name\":\"Ranger\",\"email\":\"drangery@usa.gov\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":31,\"birthday\":\"1992-07-10\"}\n{\"id\":36,\"first_name\":\"Viola\",\"last_name\":\"Lyburn\",\"email\":\"vlyburnz@bigcartel.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":14,\"birthday\":\"1993-05-06\"}\n{\"id\":37,\"first_name\":\"Stefania\",\"last_name\":\"Gee\",\"email\":\"sgee10@odnoklassniki.ru\",\"owns_house\":true,\"favorite_color\":null,\"age\":66,\"birthday\":\"1996-12-05\"}\n{\"id\":38,\"first_name\":\"Kearney\",\"last_name\":\"Tribble\",\"email\":\"ktribble11@gov.uk\",\"owns_house\":true,\"favorite_color\":null,\"age\":17,\"birthday\":\"2002-12-31\"}\n{\"id\":39,\"first_name\":\"Kippy\",\"last_name\":\"Plumptre\",\"email\":\"kplumptre12@etsy.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":69,\"birthday\":\"2006-11-28\"}\n{\"id\":40,\"first_name\":\"Tasha\",\"last_name\":\"Eite\",\"email\":\"teite13@tamu.edu\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":52,\"birthday\":\"1997-10-16\"}\n{\"id\":41,\"first_name\":\"Heall\",\"last_name\":\"Coonan\",\"email\":\"hcoonan14@ebay.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":67,\"birthday\":\"1998-02-24\"}\n{\"id\":42,\"first_name\":\"Chrissy\",\"last_name\":\"Karlicek\",\"email\":\"ckarlicek15@comsenz.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":45,\"birthday\":\"1997-11-12\"}\n{\"id\":43,\"first_name\":\"Nora\",\"last_name\":\"McCheyne\",\"email\":\"nmccheyne16@amazon.co.uk\",\"owns_house\":true,\"favorite_color\":null,\"age\":37,\"birthday\":\"1992-09-29\"}\n{\"id\":44,\"first_name\":\"Maureen\",\"last_name\":\"Jouhning\",\"email\":\"mjouhning17@amazonaws.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":10,\"birthday\":\"1996-10-22\"}\n{\"id\":45,\"first_name\":\"Dionisio\",\"last_name\":\"Matchett\",\"email\":\"dmatchett18@redcross.org\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":72,\"birthday\":\"2001-03-16\"}\n{\"id\":46,\"first_name\":\"Logan\",\"last_name\":\"Ollerenshaw\",\"email\":\"lollerenshaw19@google.com.au\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":80,\"birthday\":\"1992-01-16\"}\n{\"id\":47,\"first_name\":\"Fielding\",\"last_name\":\"Babber\",\"email\":\"fbabber1a@purevolume.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":48,\"birthday\":\"2007-05-25\"}\n{\"id\":48,\"first_name\":\"Thayne\",\"last_name\":\"Gidman\",\"email\":\"tgidman1b@whitehouse.gov\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":23,\"birthday\":\"2008-06-18\"}\n{\"id\":49,\"first_name\":\"Alon\",\"last_name\":\"Soule\",\"email\":\"asoule1c@intel.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":24,\"birthday\":\"2000-01-01\"}\n{\"id\":50,\"first_name\":\"Fair\",\"last_name\":\"Pedrocchi\",\"email\":\"fpedrocchi1d@topsy.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":9,\"birthday\":\"2006-07-16\"}\n{\"id\":51,\"first_name\":\"Virginia\",\"last_name\":\"Woolmer\",\"email\":\"vwoolmer1e@usnews.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":34,\"birthday\":\"2006-05-08\"}\n{\"id\":52,\"first_name\":\"Padriac\",\"last_name\":\"Meanwell\",\"email\":\"pmeanwell1f@wix.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":33,\"birthday\":\"2005-07-22\"}\n{\"id\":53,\"first_name\":\"Madge\",\"last_name\":\"Boarder\",\"email\":\"mboarder1g@cnet.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":41,\"birthday\":\"2009-05-14\"}\n{\"id\":54,\"first_name\":\"Esmaria\",\"last_name\":\"Alston\",\"email\":\"ealston1h@vimeo.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":39,\"birthday\":\"1995-08-21\"}\n{\"id\":55,\"first_name\":\"Grata\",\"last_name\":\"Mates\",\"email\":\"gmates1i@yahoo.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":19,\"birthday\":\"2005-05-10\"}\n{\"id\":56,\"first_name\":\"Dorie\",\"last_name\":\"Feldbaum\",\"email\":\"dfeldbaum1j@cpanel.net\",\"owns_house\":true,\"favorite_color\":null,\"age\":38,\"birthday\":\"2003-07-04\"}\n{\"id\":57,\"first_name\":\"Phaidra\",\"last_name\":\"Setchfield\",\"email\":\"psetchfield1k@dion.ne.jp\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":52,\"birthday\":\"1991-08-10\"}\n{\"id\":58,\"first_name\":\"Verile\",\"last_name\":\"Withur\",\"email\":\"vwithur1l@rambler.ru\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":25,\"birthday\":\"1999-10-11\"}\n{\"id\":59,\"first_name\":\"Meris\",\"last_name\":\"Rigby\",\"email\":\"mrigby1m@wix.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":21,\"birthday\":\"1999-10-15\"}\n{\"id\":60,\"first_name\":\"Allard\",\"last_name\":\"Dougliss\",\"email\":\"adougliss1n@constantcontact.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":1,\"birthday\":\"1998-02-18\"}\n{\"id\":61,\"first_name\":\"Conny\",\"last_name\":\"Woodfield\",\"email\":\"cwoodfield1o@upenn.edu\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":45,\"birthday\":\"1992-03-27\"}\n{\"id\":62,\"first_name\":\"Lannie\",\"last_name\":\"De Fraine\",\"email\":\"ldefraine1p@usnews.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":37,\"birthday\":\"1992-12-11\"}\n{\"id\":63,\"first_name\":\"Tedman\",\"last_name\":\"Geertje\",\"email\":\"tgeertje1q@narod.ru\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":26,\"birthday\":\"2003-04-09\"}\n{\"id\":64,\"first_name\":\"Sile\",\"last_name\":\"Donke\",\"email\":\"sdonke1r@craigslist.org\",\"owns_house\":true,\"favorite_color\":null,\"age\":30,\"birthday\":\"2005-08-31\"}\n{\"id\":65,\"first_name\":\"Cati\",\"last_name\":\"Olivas\",\"email\":\"colivas1s@businessinsider.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":13,\"birthday\":\"2003-06-08\"}\n{\"id\":66,\"first_name\":\"Robinett\",\"last_name\":\"Pauly\",\"email\":\"rpauly1t@networksolutions.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":28,\"birthday\":\"1998-02-12\"}\n{\"id\":67,\"first_name\":\"Danna\",\"last_name\":\"Kirkwood\",\"email\":\"dkirkwood1u@goo.ne.jp\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":8,\"birthday\":\"2009-03-02\"}\n{\"id\":68,\"first_name\":\"Koralle\",\"last_name\":\"McIlvoray\",\"email\":\"kmcilvoray1v@apache.org\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":64,\"birthday\":\"2003-08-15\"}\n{\"id\":69,\"first_name\":\"Sheffy\",\"last_name\":\"Paolotto\",\"email\":\"spaolotto1w@wiley.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":44,\"birthday\":\"2000-03-19\"}\n{\"id\":70,\"first_name\":\"Sadye\",\"last_name\":\"Cordeau]\",\"email\":\"scordeau1x@dyndns.org\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":13,\"birthday\":\"1991-07-30\"}\n{\"id\":71,\"first_name\":\"Sheena\",\"last_name\":\"Vogl\",\"email\":\"svogl1y@privacy.gov.au\",\"owns_house\":true,\"favorite_color\":null,\"age\":43,\"birthday\":\"2007-11-22\"}\n{\"id\":72,\"first_name\":\"Joela\",\"last_name\":\"Millichip\",\"email\":\"jmillichip1z@jugem.jp\",\"owns_house\":true,\"favorite_color\":null,\"age\":19,\"birthday\":\"2009-09-10\"}\n{\"id\":73,\"first_name\":\"Yurik\",\"last_name\":\"Snasel\",\"email\":\"ysnasel20@blogger.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":46,\"birthday\":\"1994-12-09\"}\n{\"id\":74,\"first_name\":\"Tailor\",\"last_name\":\"Exelby\",\"email\":\"texelby21@cbc.ca\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":31,\"birthday\":\"2004-09-05\"}\n{\"id\":75,\"first_name\":\"Karolina\",\"last_name\":\"Bagster\",\"email\":\"kbagster22@alibaba.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":57,\"birthday\":\"1995-03-25\"}\n{\"id\":76,\"first_name\":\"Domeniga\",\"last_name\":\"Cossar\",\"email\":\"dcossar23@comcast.net\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":66,\"birthday\":\"1999-03-14\"}\n{\"id\":77,\"first_name\":\"Olivia\",\"last_name\":\"MacRury\",\"email\":\"omacrury24@yale.edu\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":2,\"birthday\":\"2002-05-02\"}\n{\"id\":78,\"first_name\":\"Aila\",\"last_name\":\"Tuke\",\"email\":\"atuke25@webeden.co.uk\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":41,\"birthday\":\"2003-02-19\"}\n{\"id\":79,\"first_name\":\"Brnaby\",\"last_name\":\"Croot\",\"email\":\"bcroot26@istockphoto.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":38,\"birthday\":\"1990-10-26\"}\n{\"id\":80,\"first_name\":\"Josh\",\"last_name\":\"Kettlesting\",\"email\":\"jkettlesting27@g.co\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":44,\"birthday\":\"2000-02-03\"}\n{\"id\":81,\"first_name\":\"Cahra\",\"last_name\":\"McClary\",\"email\":\"cmcclary28@nature.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":75,\"birthday\":\"1995-05-07\"}\n{\"id\":82,\"first_name\":\"Cleopatra\",\"last_name\":\"Paulot\",\"email\":\"cpaulot29@businessinsider.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":51,\"birthday\":\"2003-03-17\"}\n{\"id\":83,\"first_name\":\"Konstanze\",\"last_name\":\"Hise\",\"email\":\"khise2a@reddit.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":2,\"birthday\":\"1992-10-06\"}\n{\"id\":84,\"first_name\":\"Allsun\",\"last_name\":\"Ondrusek\",\"email\":\"aondrusek2b@ning.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":7,\"birthday\":\"1999-03-16\"}\n{\"id\":85,\"first_name\":\"Zea\",\"last_name\":\"Kyte\",\"email\":\"zkyte2c@live.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":45,\"birthday\":\"2001-06-02\"}\n{\"id\":86,\"first_name\":\"Bunnie\",\"last_name\":\"Huncoot\",\"email\":\"bhuncoot2d@amazon.de\",\"owns_house\":false,\"favorite_color\":null,\"age\":8,\"birthday\":\"1995-01-20\"}\n{\"id\":87,\"first_name\":\"Booth\",\"last_name\":\"Alexsandrov\",\"email\":\"balexsandrov2e@cnet.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":40,\"birthday\":\"2003-10-30\"}\n{\"id\":88,\"first_name\":\"Cherry\",\"last_name\":\"Scogings\",\"email\":\"cscogings2f@yellowbook.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":52,\"birthday\":\"1996-01-27\"}\n{\"id\":89,\"first_name\":\"Sybille\",\"last_name\":\"Fathers\",\"email\":\"sfathers2g@squidoo.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":20,\"birthday\":\"1995-03-23\"}\n{\"id\":90,\"first_name\":\"Pamela\",\"last_name\":\"Ridewood\",\"email\":\"pridewood2h@prlog.org\",\"owns_house\":true,\"favorite_color\":null,\"age\":51,\"birthday\":\"2006-08-24\"}\n{\"id\":91,\"first_name\":\"Ashby\",\"last_name\":\"Quinevan\",\"email\":\"aquinevan2i@behance.net\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":61,\"birthday\":\"2006-02-01\"}\n{\"id\":92,\"first_name\":\"Ida\",\"last_name\":\"Riggott\",\"email\":\"iriggott2j@cdbaby.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":26,\"birthday\":\"2008-07-20\"}\n{\"id\":93,\"first_name\":\"Elmira\",\"last_name\":\"Jacquemard\",\"email\":\"ejacquemard2k@mozilla.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":67,\"birthday\":\"1992-07-04\"}\n{\"id\":94,\"first_name\":\"Bertie\",\"last_name\":\"Jeremiah\",\"email\":\"bjeremiah2l@ning.com\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":51,\"birthday\":\"2004-07-26\"}\n{\"id\":95,\"first_name\":\"Valentine\",\"last_name\":\"McLinden\",\"email\":\"vmclinden2m@wunderground.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":53,\"birthday\":\"1992-08-27\"}\n{\"id\":96,\"first_name\":\"Dun\",\"last_name\":\"De Brett\",\"email\":\"ddebrett2n@yelp.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":48,\"birthday\":\"1996-02-06\"}\n{\"id\":97,\"first_name\":\"Chrisy\",\"last_name\":\"Lamanby\",\"email\":\"clamanby2o@ted.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":77,\"birthday\":\"2005-05-07\"}\n{\"id\":98,\"first_name\":\"Helen-elizabeth\",\"last_name\":\"Marquess\",\"email\":\"hmarquess2p@mapquest.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":65,\"birthday\":\"1997-11-26\"}\n{\"id\":99,\"first_name\":\"Alberik\",\"last_name\":\"Hardeman\",\"email\":\"ahardeman2q@blogspot.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":42,\"birthday\":\"2005-09-29\"}\n{\"id\":100,\"first_name\":\"Tarrance\",\"last_name\":\"Lauder\",\"email\":\"tlauder2r@360.cn\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":59,\"birthday\":\"2009-04-29\"}\n{\"id\":101,\"first_name\":\"Ad\",\"last_name\":\"Eyckelbeck\",\"email\":\"aeyckelbeck2s@friendfeed.com\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":3,\"birthday\":\"2007-12-31\"}\n{\"id\":102,\"first_name\":\"Eli\",\"last_name\":\"Catherick\",\"email\":\"ecatherick2t@joomla.org\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":2,\"birthday\":\"2009-12-10\"}\n{\"id\":103,\"first_name\":\"Pembroke\",\"last_name\":\"Hadlee\",\"email\":\"phadlee2u@go.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":45,\"birthday\":\"2001-03-14\"}\n{\"id\":104,\"first_name\":\"Mitchell\",\"last_name\":\"Jenton\",\"email\":\"mjenton2v@studiopress.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":53,\"birthday\":\"2007-09-13\"}\n{\"id\":105,\"first_name\":\"Abigail\",\"last_name\":\"Cholmondeley\",\"email\":\"acholmondeley2w@intel.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":58,\"birthday\":\"2001-03-01\"}\n{\"id\":106,\"first_name\":\"Daveen\",\"last_name\":\"Stoite\",\"email\":\"dstoite2x@plala.or.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":2,\"birthday\":\"1996-09-10\"}\n{\"id\":107,\"first_name\":\"Ward\",\"last_name\":\"Pedri\",\"email\":\"wpedri2y@artisteer.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":75,\"birthday\":\"2009-04-07\"}\n{\"id\":108,\"first_name\":\"Cahra\",\"last_name\":\"Donativo\",\"email\":\"cdonativo2z@newsvine.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":14,\"birthday\":\"2009-08-16\"}\n{\"id\":109,\"first_name\":\"Fayth\",\"last_name\":\"Dulinty\",\"email\":\"fdulinty30@sciencedirect.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":68,\"birthday\":\"1998-02-05\"}\n{\"id\":110,\"first_name\":\"Andras\",\"last_name\":\"Bondy\",\"email\":\"abondy31@smugmug.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":75,\"birthday\":\"2006-04-08\"}\n{\"id\":111,\"first_name\":\"Rene\",\"last_name\":\"Breissan\",\"email\":\"rbreissan32@latimes.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":20,\"birthday\":\"2007-07-15\"}\n{\"id\":112,\"first_name\":\"Burton\",\"last_name\":\"Keoghan\",\"email\":\"bkeoghan33@jugem.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":80,\"birthday\":\"2006-01-25\"}\n{\"id\":113,\"first_name\":\"Erek\",\"last_name\":\"Mattiessen\",\"email\":\"emattiessen34@woothemes.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":54,\"birthday\":\"2000-10-15\"}\n{\"id\":114,\"first_name\":\"Wallas\",\"last_name\":\"Gebhard\",\"email\":\"wgebhard35@nsw.gov.au\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":9,\"birthday\":\"2001-12-06\"}\n{\"id\":115,\"first_name\":\"Yuri\",\"last_name\":\"Isakowicz\",\"email\":\"yisakowicz36@zimbio.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":46,\"birthday\":\"1990-07-03\"}\n{\"id\":116,\"first_name\":\"Earl\",\"last_name\":\"Chastey\",\"email\":\"echastey37@nasa.gov\",\"owns_house\":false,\"favorite_color\":null,\"age\":28,\"birthday\":\"1991-02-13\"}\n{\"id\":117,\"first_name\":\"Kristos\",\"last_name\":\"Wartonby\",\"email\":\"kwartonby38@google.pl\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":18,\"birthday\":\"2001-04-07\"}\n{\"id\":118,\"first_name\":\"Sukey\",\"last_name\":\"Beresford\",\"email\":\"sberesford39@sina.com.cn\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":75,\"birthday\":\"1999-02-04\"}\n{\"id\":119,\"first_name\":\"Sondra\",\"last_name\":\"Marrow\",\"email\":\"smarrow3a@merriam-webster.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":79,\"birthday\":\"2008-10-22\"}\n{\"id\":120,\"first_name\":\"Wylie\",\"last_name\":\"Shrimpton\",\"email\":\"wshrimpton3b@mail.ru\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":78,\"birthday\":\"2006-08-12\"}\n{\"id\":121,\"first_name\":\"Blondelle\",\"last_name\":\"Crees\",\"email\":\"bcrees3c@house.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":9,\"birthday\":\"2002-09-23\"}\n{\"id\":122,\"first_name\":\"Enos\",\"last_name\":\"Side\",\"email\":\"eside3d@wiley.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":38,\"birthday\":\"2001-06-12\"}\n{\"id\":123,\"first_name\":\"Lanae\",\"last_name\":\"Colchett\",\"email\":\"lcolchett3e@mlb.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":38,\"birthday\":\"2002-08-01\"}\n{\"id\":124,\"first_name\":\"Brendon\",\"last_name\":\"Capstake\",\"email\":\"bcapstake3f@constantcontact.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":78,\"birthday\":\"2002-08-05\"}\n{\"id\":125,\"first_name\":\"Eugenio\",\"last_name\":\"Merrigan\",\"email\":\"emerrigan3g@hugedomains.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":68,\"birthday\":\"1992-03-31\"}\n{\"id\":126,\"first_name\":\"Marlo\",\"last_name\":\"Kamiyama\",\"email\":\"mkamiyama3h@guardian.co.uk\",\"owns_house\":true,\"favorite_color\":null,\"age\":4,\"birthday\":\"2001-12-17\"}\n{\"id\":127,\"first_name\":\"Wynne\",\"last_name\":\"Hendrickx\",\"email\":\"whendrickx3i@icq.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":24,\"birthday\":\"2007-02-25\"}\n{\"id\":128,\"first_name\":\"Arluene\",\"last_name\":\"Fedorski\",\"email\":\"afedorski3j@answers.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":70,\"birthday\":\"1995-11-16\"}\n{\"id\":129,\"first_name\":\"Noella\",\"last_name\":\"Roz\",\"email\":\"nroz3k@sourceforge.net\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":13,\"birthday\":\"1997-08-26\"}\n{\"id\":130,\"first_name\":\"Emmit\",\"last_name\":\"Paute\",\"email\":\"epaute3l@jiathis.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":69,\"birthday\":\"1995-12-04\"}\n{\"id\":131,\"first_name\":\"Jobi\",\"last_name\":\"Hawkridge\",\"email\":\"jhawkridge3m@163.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":43,\"birthday\":\"2004-04-08\"}\n{\"id\":132,\"first_name\":\"Cchaddie\",\"last_name\":\"Diegan\",\"email\":\"cdiegan3n@rambler.ru\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":34,\"birthday\":\"2008-11-25\"}\n{\"id\":133,\"first_name\":\"Vale\",\"last_name\":\"Simond\",\"email\":\"vsimond3o@google.it\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":19,\"birthday\":\"2001-03-11\"}\n{\"id\":134,\"first_name\":\"Daisi\",\"last_name\":\"Parkhouse\",\"email\":\"dparkhouse3p@forbes.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":60,\"birthday\":\"1995-04-13\"}\n{\"id\":135,\"first_name\":\"Noll\",\"last_name\":\"Chicken\",\"email\":\"nchicken3q@opera.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":71,\"birthday\":\"1996-04-07\"}\n{\"id\":136,\"first_name\":\"Danika\",\"last_name\":\"Pethrick\",\"email\":\"dpethrick3r@businesswire.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":50,\"birthday\":\"2003-02-08\"}\n{\"id\":137,\"first_name\":\"Rina\",\"last_name\":\"Course\",\"email\":\"rcourse3s@studiopress.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":44,\"birthday\":\"1993-07-10\"}\n{\"id\":138,\"first_name\":\"Francois\",\"last_name\":\"Cheater\",\"email\":\"fcheater3t@webnode.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":43,\"birthday\":\"1997-07-13\"}\n{\"id\":139,\"first_name\":\"Loria\",\"last_name\":\"Rosthorn\",\"email\":\"lrosthorn3u@booking.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":1,\"birthday\":\"2004-01-26\"}\n{\"id\":140,\"first_name\":\"Kayne\",\"last_name\":\"Cawse\",\"email\":\"kcawse3v@symantec.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":63,\"birthday\":\"2009-10-27\"}\n{\"id\":141,\"first_name\":\"Codi\",\"last_name\":\"Sarrell\",\"email\":\"csarrell3w@photobucket.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":60,\"birthday\":\"1995-10-06\"}\n{\"id\":142,\"first_name\":\"Phyllida\",\"last_name\":\"Muehle\",\"email\":\"pmuehle3x@imageshack.us\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":26,\"birthday\":\"1996-04-28\"}\n{\"id\":143,\"first_name\":\"Mick\",\"last_name\":\"Rampton\",\"email\":\"mrampton3y@upenn.edu\",\"owns_house\":true,\"favorite_color\":null,\"age\":28,\"birthday\":\"1990-07-04\"}\n{\"id\":144,\"first_name\":\"Arlen\",\"last_name\":\"Tolumello\",\"email\":\"atolumello3z@telegraph.co.uk\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":32,\"birthday\":\"2006-10-02\"}\n{\"id\":145,\"first_name\":\"Hoebart\",\"last_name\":\"Wyss\",\"email\":\"hwyss40@slate.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":39,\"birthday\":\"2001-06-01\"}\n{\"id\":146,\"first_name\":\"Jacinda\",\"last_name\":\"Eversley\",\"email\":\"jeversley41@hubpages.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":3,\"birthday\":\"2000-12-22\"}\n{\"id\":147,\"first_name\":\"Rab\",\"last_name\":\"Hothersall\",\"email\":\"rhothersall42@example.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":51,\"birthday\":\"1993-07-21\"}\n{\"id\":148,\"first_name\":\"Pauline\",\"last_name\":\"Wassell\",\"email\":\"pwassell43@lycos.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":72,\"birthday\":\"1991-04-14\"}\n{\"id\":149,\"first_name\":\"Noellyn\",\"last_name\":\"Onions\",\"email\":\"nonions44@noaa.gov\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":23,\"birthday\":\"1994-08-24\"}\n{\"id\":150,\"first_name\":\"Emmye\",\"last_name\":\"Martinetto\",\"email\":\"emartinetto45@answers.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":71,\"birthday\":\"2008-11-29\"}\n{\"id\":151,\"first_name\":\"Alic\",\"last_name\":\"Lyptrit\",\"email\":\"alyptrit46@tiny.cc\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":4,\"birthday\":\"1992-04-11\"}\n{\"id\":152,\"first_name\":\"Bobby\",\"last_name\":\"Stiegars\",\"email\":\"bstiegars47@networkadvertising.org\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":25,\"birthday\":\"1997-08-31\"}\n{\"id\":153,\"first_name\":\"Rochelle\",\"last_name\":\"Signorelli\",\"email\":\"rsignorelli48@cdc.gov\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":38,\"birthday\":\"2007-10-19\"}\n{\"id\":154,\"first_name\":\"Torr\",\"last_name\":\"Notti\",\"email\":\"tnotti49@odnoklassniki.ru\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":32,\"birthday\":\"1992-02-14\"}\n{\"id\":155,\"first_name\":\"Farlay\",\"last_name\":\"Lonsdale\",\"email\":\"flonsdale4a@washington.edu\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":77,\"birthday\":\"1994-08-25\"}\n{\"id\":156,\"first_name\":\"Gonzales\",\"last_name\":\"Syson\",\"email\":\"gsyson4b@npr.org\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":12,\"birthday\":\"1994-02-03\"}\n{\"id\":157,\"first_name\":\"Jessika\",\"last_name\":\"Banasevich\",\"email\":\"jbanasevich4c@seattletimes.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":42,\"birthday\":\"2002-10-27\"}\n{\"id\":158,\"first_name\":\"Karna\",\"last_name\":\"Lukianovich\",\"email\":\"klukianovich4d@webnode.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":53,\"birthday\":\"2009-08-12\"}\n{\"id\":159,\"first_name\":\"Jodie\",\"last_name\":\"Molder\",\"email\":\"jmolder4e@livejournal.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":28,\"birthday\":\"1996-09-08\"}\n{\"id\":160,\"first_name\":\"Denis\",\"last_name\":\"Laborda\",\"email\":\"dlaborda4f@berkeley.edu\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":57,\"birthday\":\"1997-11-06\"}\n{\"id\":161,\"first_name\":\"Halette\",\"last_name\":\"Exell\",\"email\":\"hexell4g@rambler.ru\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":5,\"birthday\":\"2006-05-21\"}\n{\"id\":162,\"first_name\":\"Abbey\",\"last_name\":\"Claw\",\"email\":\"aclaw4h@apache.org\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":21,\"birthday\":\"1990-10-30\"}\n{\"id\":163,\"first_name\":\"Ethelred\",\"last_name\":\"Heaps\",\"email\":\"eheaps4i@phoca.cz\",\"owns_house\":false,\"favorite_color\":null,\"age\":31,\"birthday\":\"2002-07-29\"}\n{\"id\":164,\"first_name\":\"Jozef\",\"last_name\":\"Delcastel\",\"email\":\"jdelcastel4j@biglobe.ne.jp\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":14,\"birthday\":\"2004-04-01\"}\n{\"id\":165,\"first_name\":\"Ogdan\",\"last_name\":\"Kingshott\",\"email\":\"okingshott4k@hp.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":27,\"birthday\":\"1997-04-10\"}\n{\"id\":166,\"first_name\":\"Cheri\",\"last_name\":\"Dome\",\"email\":\"cdome4l@ft.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":39,\"birthday\":\"1999-05-27\"}\n{\"id\":167,\"first_name\":\"Alonzo\",\"last_name\":\"Licquorish\",\"email\":\"alicquorish4m@eepurl.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":66,\"birthday\":\"2007-10-07\"}\n{\"id\":168,\"first_name\":\"Fidelio\",\"last_name\":\"Potebury\",\"email\":\"fpotebury4n@samsung.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":79,\"birthday\":\"1991-04-01\"}\n{\"id\":169,\"first_name\":\"Robbert\",\"last_name\":\"Ygo\",\"email\":\"rygo4o@washington.edu\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":45,\"birthday\":\"1995-06-03\"}\n{\"id\":170,\"first_name\":\"Wells\",\"last_name\":\"Tybalt\",\"email\":\"wtybalt4p@cpanel.net\",\"owns_house\":false,\"favorite_color\":null,\"age\":29,\"birthday\":\"1993-07-28\"}\n{\"id\":171,\"first_name\":\"Marta\",\"last_name\":\"Dowell\",\"email\":\"mdowell4q@hatena.ne.jp\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":27,\"birthday\":\"2005-01-30\"}\n{\"id\":172,\"first_name\":\"Colleen\",\"last_name\":\"Tonbye\",\"email\":\"ctonbye4r@list-manage.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":7,\"birthday\":\"1996-06-04\"}\n{\"id\":173,\"first_name\":\"Martie\",\"last_name\":\"Simonin\",\"email\":\"msimonin4s@youtu.be\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":42,\"birthday\":\"2005-11-12\"}\n{\"id\":174,\"first_name\":\"Korry\",\"last_name\":\"Huxstep\",\"email\":\"khuxstep4t@phpbb.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":34,\"birthday\":\"2003-07-03\"}\n{\"id\":175,\"first_name\":\"Barbra\",\"last_name\":\"Sillwood\",\"email\":\"bsillwood4u@dropbox.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":60,\"birthday\":\"1994-08-18\"}\n{\"id\":176,\"first_name\":\"Kellina\",\"last_name\":\"Murby\",\"email\":\"kmurby4v@ebay.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":69,\"birthday\":\"2006-04-18\"}\n{\"id\":177,\"first_name\":\"Tobit\",\"last_name\":\"Kneeland\",\"email\":\"tkneeland4w@icio.us\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":11,\"birthday\":\"1997-08-26\"}\n{\"id\":178,\"first_name\":\"Ethelind\",\"last_name\":\"Shivell\",\"email\":\"eshivell4x@github.io\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":58,\"birthday\":\"1991-08-06\"}\n{\"id\":179,\"first_name\":\"Galvin\",\"last_name\":\"Vernazza\",\"email\":\"gvernazza4y@icq.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":8,\"birthday\":\"1998-12-16\"}\n{\"id\":180,\"first_name\":\"Marijo\",\"last_name\":\"Lackeye\",\"email\":\"mlackeye4z@goodreads.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":32,\"birthday\":\"2009-04-18\"}\n{\"id\":181,\"first_name\":\"Woodie\",\"last_name\":\"Edgerly\",\"email\":\"wedgerly50@mozilla.org\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":35,\"birthday\":\"1995-06-28\"}\n{\"id\":182,\"first_name\":\"Igor\",\"last_name\":\"Jarrad\",\"email\":\"ijarrad51@phoca.cz\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":79,\"birthday\":\"1995-09-10\"}\n{\"id\":183,\"first_name\":\"Richard\",\"last_name\":\"McLaren\",\"email\":\"rmclaren52@twitter.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":40,\"birthday\":\"1995-11-12\"}\n{\"id\":184,\"first_name\":\"Orelie\",\"last_name\":\"Evill\",\"email\":\"oevill53@ifeng.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":50,\"birthday\":\"1995-09-20\"}\n{\"id\":185,\"first_name\":\"Remington\",\"last_name\":\"Lethebridge\",\"email\":\"rlethebridge54@sciencedirect.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":47,\"birthday\":\"1994-04-28\"}\n{\"id\":186,\"first_name\":\"Jorie\",\"last_name\":\"Freezer\",\"email\":\"jfreezer55@skype.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":20,\"birthday\":\"2004-12-06\"}\n{\"id\":187,\"first_name\":\"Reinhold\",\"last_name\":\"Depka\",\"email\":\"rdepka56@cnbc.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":67,\"birthday\":\"2008-09-08\"}\n{\"id\":188,\"first_name\":\"Craig\",\"last_name\":\"Elvish\",\"email\":\"celvish57@de.vu\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":43,\"birthday\":\"1991-04-06\"}\n{\"id\":189,\"first_name\":\"Tiffany\",\"last_name\":\"Bonsale\",\"email\":\"tbonsale58@sphinn.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":36,\"birthday\":\"2006-04-12\"}\n{\"id\":190,\"first_name\":\"Denys\",\"last_name\":\"Madrell\",\"email\":\"dmadrell59@slashdot.org\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":62,\"birthday\":\"2002-04-19\"}\n{\"id\":191,\"first_name\":\"Kennan\",\"last_name\":\"Scrine\",\"email\":\"kscrine5a@ftc.gov\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":1,\"birthday\":\"2000-01-25\"}\n{\"id\":192,\"first_name\":\"Nikkie\",\"last_name\":\"Netti\",\"email\":\"nnetti5b@miitbeian.gov.cn\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":39,\"birthday\":\"1999-10-11\"}\n{\"id\":193,\"first_name\":\"Lenci\",\"last_name\":\"Goade\",\"email\":\"lgoade5c@google.es\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":31,\"birthday\":\"2002-09-12\"}\n{\"id\":194,\"first_name\":\"Val\",\"last_name\":\"Tomasino\",\"email\":\"vtomasino5d@cbsnews.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":27,\"birthday\":\"1990-05-15\"}\n{\"id\":195,\"first_name\":\"Jolyn\",\"last_name\":\"Pretorius\",\"email\":\"jpretorius5e@sbwire.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":30,\"birthday\":\"1991-10-11\"}\n{\"id\":196,\"first_name\":\"Quinta\",\"last_name\":\"Bordes\",\"email\":\"qbordes5f@npr.org\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":63,\"birthday\":\"2000-06-03\"}\n{\"id\":197,\"first_name\":\"Raquela\",\"last_name\":\"McGown\",\"email\":\"rmcgown5g@naver.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":21,\"birthday\":\"2008-11-28\"}\n{\"id\":198,\"first_name\":\"Bunny\",\"last_name\":\"Dirkin\",\"email\":\"bdirkin5h@usgs.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":4,\"birthday\":\"2001-01-12\"}\n{\"id\":199,\"first_name\":\"Dale\",\"last_name\":\"Grimwood\",\"email\":\"dgrimwood5i@dagondesign.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":74,\"birthday\":\"1992-11-07\"}\n{\"id\":200,\"first_name\":\"Pall\",\"last_name\":\"Duffell\",\"email\":\"pduffell5j@is.gd\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":13,\"birthday\":\"1993-01-07\"}\n{\"id\":201,\"first_name\":\"Leonie\",\"last_name\":\"Viant\",\"email\":\"lviant5k@xrea.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":52,\"birthday\":\"1991-02-21\"}\n{\"id\":202,\"first_name\":\"Calla\",\"last_name\":\"McCobb\",\"email\":\"cmccobb5l@altervista.org\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":55,\"birthday\":\"2007-02-20\"}\n{\"id\":203,\"first_name\":\"Whitman\",\"last_name\":\"Glowach\",\"email\":\"wglowach5m@plala.or.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":69,\"birthday\":\"1997-09-13\"}\n{\"id\":204,\"first_name\":\"Siusan\",\"last_name\":\"Alpine\",\"email\":\"salpine5n@gmpg.org\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":35,\"birthday\":\"1998-08-10\"}\n{\"id\":205,\"first_name\":\"Roman\",\"last_name\":\"Hearse\",\"email\":\"rhearse5o@nbcnews.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":36,\"birthday\":\"1998-12-21\"}\n{\"id\":206,\"first_name\":\"Mikol\",\"last_name\":\"Piotrowski\",\"email\":\"mpiotrowski5p@cmu.edu\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":48,\"birthday\":\"1996-02-29\"}\n{\"id\":207,\"first_name\":\"Agretha\",\"last_name\":\"Doran\",\"email\":\"adoran5q@rakuten.co.jp\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":77,\"birthday\":\"1993-01-31\"}\n{\"id\":208,\"first_name\":\"Konstance\",\"last_name\":\"Larose\",\"email\":\"klarose5r@e-recht24.de\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":32,\"birthday\":\"2002-02-17\"}\n{\"id\":209,\"first_name\":\"Ivonne\",\"last_name\":\"Denyer\",\"email\":\"idenyer5s@disqus.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":41,\"birthday\":\"2004-01-17\"}\n{\"id\":210,\"first_name\":\"Daphne\",\"last_name\":\"Heyns\",\"email\":\"dheyns5t@vistaprint.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":40,\"birthday\":\"1995-04-17\"}\n{\"id\":211,\"first_name\":\"Mandy\",\"last_name\":\"Ray\",\"email\":\"mray5u@ustream.tv\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":47,\"birthday\":\"2005-11-06\"}\n{\"id\":212,\"first_name\":\"Eolande\",\"last_name\":\"Blann\",\"email\":\"eblann5v@marketwatch.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":55,\"birthday\":\"1997-01-22\"}\n{\"id\":213,\"first_name\":\"Melita\",\"last_name\":\"Goodlett\",\"email\":\"mgoodlett5w@mlb.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":44,\"birthday\":\"1998-11-08\"}\n{\"id\":214,\"first_name\":\"Donielle\",\"last_name\":\"Merricks\",\"email\":\"dmerricks5x@state.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":63,\"birthday\":\"2003-11-25\"}\n{\"id\":215,\"first_name\":\"Arlan\",\"last_name\":\"Watford\",\"email\":\"awatford5y@multiply.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":22,\"birthday\":\"2000-09-27\"}\n{\"id\":216,\"first_name\":\"Sherrie\",\"last_name\":\"Scarrott\",\"email\":\"sscarrott5z@china.com.cn\",\"owns_house\":true,\"favorite_color\":null,\"age\":2,\"birthday\":\"2005-10-23\"}\n{\"id\":217,\"first_name\":\"Cherrita\",\"last_name\":\"Bonar\",\"email\":\"cbonar60@marketwatch.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":57,\"birthday\":\"2008-09-16\"}\n{\"id\":218,\"first_name\":\"Kizzie\",\"last_name\":\"Speedin\",\"email\":\"kspeedin61@hao123.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":20,\"birthday\":\"2005-05-09\"}\n{\"id\":219,\"first_name\":\"Teresina\",\"last_name\":\"Kondratenya\",\"email\":\"tkondratenya62@naver.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":43,\"birthday\":\"1991-10-28\"}\n{\"id\":220,\"first_name\":\"Merna\",\"last_name\":\"Hargitt\",\"email\":\"mhargitt63@51.la\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":4,\"birthday\":\"1995-09-17\"}\n{\"id\":221,\"first_name\":\"Isacco\",\"last_name\":\"Ondrusek\",\"email\":\"iondrusek64@4shared.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":31,\"birthday\":\"1991-11-21\"}\n{\"id\":222,\"first_name\":\"Jacinthe\",\"last_name\":\"Roselli\",\"email\":\"jroselli65@yolasite.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":60,\"birthday\":\"2005-12-05\"}\n{\"id\":223,\"first_name\":\"Ibrahim\",\"last_name\":\"Mitcham\",\"email\":\"imitcham66@ted.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":77,\"birthday\":\"1991-05-03\"}\n{\"id\":224,\"first_name\":\"Mikaela\",\"last_name\":\"Bern\",\"email\":\"mbern67@amazon.de\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":67,\"birthday\":\"1997-06-15\"}\n{\"id\":225,\"first_name\":\"Kevin\",\"last_name\":\"Barkess\",\"email\":\"kbarkess68@people.com.cn\",\"owns_house\":false,\"favorite_color\":null,\"age\":4,\"birthday\":\"1999-10-17\"}\n{\"id\":226,\"first_name\":\"Lindie\",\"last_name\":\"Haskur\",\"email\":\"lhaskur69@google.com.br\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":75,\"birthday\":\"2006-08-17\"}\n{\"id\":227,\"first_name\":\"Dorolisa\",\"last_name\":\"Seeger\",\"email\":\"dseeger6a@wiley.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":74,\"birthday\":\"1992-09-07\"}\n{\"id\":228,\"first_name\":\"Erinna\",\"last_name\":\"Ackhurst\",\"email\":\"eackhurst6b@goodreads.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":30,\"birthday\":\"2002-09-23\"}\n{\"id\":229,\"first_name\":\"Caresse\",\"last_name\":\"Sommerlie\",\"email\":\"csommerlie6c@ifeng.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":47,\"birthday\":\"1993-03-16\"}\n{\"id\":230,\"first_name\":\"Demeter\",\"last_name\":\"Bru\",\"email\":\"dbru6d@surveymonkey.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":45,\"birthday\":\"1994-10-12\"}\n{\"id\":231,\"first_name\":\"Demetris\",\"last_name\":\"Wittleton\",\"email\":\"dwittleton6e@slate.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":10,\"birthday\":\"2003-01-31\"}\n{\"id\":232,\"first_name\":\"Carroll\",\"last_name\":\"Lawfull\",\"email\":\"clawfull6f@elpais.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":60,\"birthday\":\"2001-10-06\"}\n{\"id\":233,\"first_name\":\"Hedvig\",\"last_name\":\"Vasilyevski\",\"email\":\"hvasilyevski6g@lycos.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":10,\"birthday\":\"1990-12-03\"}\n{\"id\":234,\"first_name\":\"Tybalt\",\"last_name\":\"Elkins\",\"email\":\"telkins6h@ezinearticles.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":8,\"birthday\":\"1999-08-09\"}\n{\"id\":235,\"first_name\":\"Wesley\",\"last_name\":\"Todarini\",\"email\":\"wtodarini6i@phpbb.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":74,\"birthday\":\"2003-02-27\"}\n{\"id\":236,\"first_name\":\"Yasmin\",\"last_name\":\"Harverson\",\"email\":\"yharverson6j@livejournal.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":75,\"birthday\":\"1991-11-09\"}\n{\"id\":237,\"first_name\":\"Forest\",\"last_name\":\"Benard\",\"email\":\"fbenard6k@fc2.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":26,\"birthday\":\"2007-05-22\"}\n{\"id\":238,\"first_name\":\"Elwyn\",\"last_name\":\"Schade\",\"email\":\"eschade6l@utexas.edu\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":54,\"birthday\":\"2009-10-31\"}\n{\"id\":239,\"first_name\":\"Maynard\",\"last_name\":\"Risely\",\"email\":\"mrisely6m@springer.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":46,\"birthday\":\"1999-12-27\"}\n{\"id\":240,\"first_name\":\"Kirbee\",\"last_name\":\"Jobbing\",\"email\":\"kjobbing6n@issuu.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":77,\"birthday\":\"1997-01-15\"}\n{\"id\":241,\"first_name\":\"Vally\",\"last_name\":\"Kiggel\",\"email\":\"vkiggel6o@independent.co.uk\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":57,\"birthday\":\"1995-03-27\"}\n{\"id\":242,\"first_name\":\"Angie\",\"last_name\":\"Bofield\",\"email\":\"abofield6p@hatena.ne.jp\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":66,\"birthday\":\"1992-01-29\"}\n{\"id\":243,\"first_name\":\"Vernen\",\"last_name\":\"Kuhlmey\",\"email\":\"vkuhlmey6q@tripod.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1997-08-05\"}\n{\"id\":244,\"first_name\":\"Ingemar\",\"last_name\":\"Khotler\",\"email\":\"ikhotler6r@google.fr\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":7,\"birthday\":\"1999-10-27\"}\n{\"id\":245,\"first_name\":\"Tudor\",\"last_name\":\"Maffioletti\",\"email\":\"tmaffioletti6s@google.cn\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":79,\"birthday\":\"1993-07-18\"}\n{\"id\":246,\"first_name\":\"Allys\",\"last_name\":\"Kalinowsky\",\"email\":\"akalinowsky6t@usnews.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":72,\"birthday\":\"1994-06-04\"}\n{\"id\":247,\"first_name\":\"Keven\",\"last_name\":\"Mougin\",\"email\":\"kmougin6u@seesaa.net\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":70,\"birthday\":\"1991-06-28\"}\n{\"id\":248,\"first_name\":\"Bert\",\"last_name\":\"Held\",\"email\":\"bheld6v@upenn.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":4,\"birthday\":\"2000-05-27\"}\n{\"id\":249,\"first_name\":\"Thaxter\",\"last_name\":\"Fernandez\",\"email\":\"tfernandez6w@dedecms.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":64,\"birthday\":\"1996-12-04\"}\n{\"id\":250,\"first_name\":\"Harriet\",\"last_name\":\"Claxton\",\"email\":\"hclaxton6x@prweb.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":5,\"birthday\":\"2002-01-06\"}\n{\"id\":251,\"first_name\":\"Mendie\",\"last_name\":\"Longforth\",\"email\":\"mlongforth6y@netvibes.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":39,\"birthday\":\"2002-11-15\"}\n{\"id\":252,\"first_name\":\"Faustina\",\"last_name\":\"Wreak\",\"email\":\"fwreak6z@furl.net\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":12,\"birthday\":\"2005-10-07\"}\n{\"id\":253,\"first_name\":\"Saleem\",\"last_name\":\"Hedlestone\",\"email\":\"shedlestone70@last.fm\",\"owns_house\":true,\"favorite_color\":null,\"age\":1,\"birthday\":\"1992-02-15\"}\n{\"id\":254,\"first_name\":\"Raf\",\"last_name\":\"Gaddas\",\"email\":\"rgaddas71@miitbeian.gov.cn\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":43,\"birthday\":\"2005-12-13\"}\n{\"id\":255,\"first_name\":\"Arnold\",\"last_name\":\"Gowling\",\"email\":\"agowling72@unblog.fr\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":36,\"birthday\":\"1992-05-27\"}\n{\"id\":256,\"first_name\":\"Gage\",\"last_name\":\"Shutle\",\"email\":\"gshutle73@facebook.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":52,\"birthday\":\"2001-12-31\"}\n{\"id\":257,\"first_name\":\"Marcel\",\"last_name\":\"Dreossi\",\"email\":\"mdreossi74@foxnews.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":50,\"birthday\":\"2000-06-01\"}\n{\"id\":258,\"first_name\":\"Samuel\",\"last_name\":\"Sweeten\",\"email\":\"ssweeten75@virginia.edu\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":8,\"birthday\":\"1998-11-20\"}\n{\"id\":259,\"first_name\":\"Peta\",\"last_name\":\"Oppie\",\"email\":\"poppie76@omniture.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":58,\"birthday\":\"1994-09-30\"}\n{\"id\":260,\"first_name\":\"Reagen\",\"last_name\":\"Beddows\",\"email\":\"rbeddows77@merriam-webster.com\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":24,\"birthday\":\"2002-04-02\"}\n{\"id\":261,\"first_name\":\"Kelcy\",\"last_name\":\"Townshend\",\"email\":\"ktownshend78@diigo.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":49,\"birthday\":\"2008-12-28\"}\n{\"id\":262,\"first_name\":\"Orelle\",\"last_name\":\"Amphlett\",\"email\":\"oamphlett79@github.io\",\"owns_house\":false,\"favorite_color\":null,\"age\":45,\"birthday\":\"2006-09-23\"}\n{\"id\":263,\"first_name\":\"Ludovika\",\"last_name\":\"Boulstridge\",\"email\":\"lboulstridge7a@uol.com.br\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":65,\"birthday\":\"2009-05-31\"}\n{\"id\":264,\"first_name\":\"Revkah\",\"last_name\":\"Harding\",\"email\":\"rharding7b@dagondesign.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":26,\"birthday\":\"2003-11-27\"}\n{\"id\":265,\"first_name\":\"Kamillah\",\"last_name\":\"Beausang\",\"email\":\"kbeausang7c@amazon.de\",\"owns_house\":false,\"favorite_color\":null,\"age\":53,\"birthday\":\"2003-08-25\"}\n{\"id\":266,\"first_name\":\"Minda\",\"last_name\":\"Cattlow\",\"email\":\"mcattlow7d@who.int\",\"owns_house\":false,\"favorite_color\":null,\"age\":12,\"birthday\":\"2007-03-13\"}\n{\"id\":267,\"first_name\":\"Waldo\",\"last_name\":\"Ivanchin\",\"email\":\"wivanchin7e@cdbaby.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":22,\"birthday\":\"2005-04-12\"}\n{\"id\":268,\"first_name\":\"Freddi\",\"last_name\":\"Atwell\",\"email\":\"fatwell7f@earthlink.net\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":52,\"birthday\":\"1998-07-27\"}\n{\"id\":269,\"first_name\":\"Gilda\",\"last_name\":\"Hallsworth\",\"email\":\"ghallsworth7g@npr.org\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":50,\"birthday\":\"1994-02-02\"}\n{\"id\":270,\"first_name\":\"Bentlee\",\"last_name\":\"Carlisso\",\"email\":\"bcarlisso7h@aol.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":46,\"birthday\":\"2009-03-10\"}\n{\"id\":271,\"first_name\":\"Norry\",\"last_name\":\"Cronkshaw\",\"email\":\"ncronkshaw7i@slashdot.org\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":50,\"birthday\":\"1995-11-10\"}\n{\"id\":272,\"first_name\":\"Federica\",\"last_name\":\"Peter\",\"email\":\"fpeter7j@ibm.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":8,\"birthday\":\"2000-10-22\"}\n{\"id\":273,\"first_name\":\"Glenn\",\"last_name\":\"Binton\",\"email\":\"gbinton7k@oracle.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":48,\"birthday\":\"1993-10-06\"}\n{\"id\":274,\"first_name\":\"Cull\",\"last_name\":\"Sedgwick\",\"email\":\"csedgwick7l@360.cn\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":34,\"birthday\":\"1992-09-19\"}\n{\"id\":275,\"first_name\":\"Marylinda\",\"last_name\":\"Klemke\",\"email\":\"mklemke7m@ning.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":70,\"birthday\":\"2007-03-29\"}\n{\"id\":276,\"first_name\":\"Melissa\",\"last_name\":\"Kain\",\"email\":\"mkain7n@technorati.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":40,\"birthday\":\"2007-04-26\"}\n{\"id\":277,\"first_name\":\"Gwenore\",\"last_name\":\"Hirsch\",\"email\":\"ghirsch7o@surveymonkey.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":70,\"birthday\":\"2001-09-01\"}\n{\"id\":278,\"first_name\":\"Sherilyn\",\"last_name\":\"Gommes\",\"email\":\"sgommes7p@lulu.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":47,\"birthday\":\"1993-05-03\"}\n{\"id\":279,\"first_name\":\"Michal\",\"last_name\":\"Janout\",\"email\":\"mjanout7q@hostgator.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":12,\"birthday\":\"2002-06-20\"}\n{\"id\":280,\"first_name\":\"Ree\",\"last_name\":\"Wardingley\",\"email\":\"rwardingley7r@mozilla.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":38,\"birthday\":\"1996-07-31\"}\n{\"id\":281,\"first_name\":\"Ericha\",\"last_name\":\"Coventry\",\"email\":\"ecoventry7s@soup.io\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":55,\"birthday\":\"2009-07-21\"}\n{\"id\":282,\"first_name\":\"Menard\",\"last_name\":\"Botly\",\"email\":\"mbotly7t@taobao.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":71,\"birthday\":\"1998-06-29\"}\n{\"id\":283,\"first_name\":\"Fabian\",\"last_name\":\"Ivashintsov\",\"email\":\"fivashintsov7u@kickstarter.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":46,\"birthday\":\"1997-04-07\"}\n{\"id\":284,\"first_name\":\"Dulcine\",\"last_name\":\"Harvard\",\"email\":\"dharvard7v@biglobe.ne.jp\",\"owns_house\":true,\"favorite_color\":null,\"age\":48,\"birthday\":\"2010-01-02\"}\n{\"id\":285,\"first_name\":\"Archie\",\"last_name\":\"Kalinsky\",\"email\":\"akalinsky7w@netlog.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":70,\"birthday\":\"1998-07-22\"}\n{\"id\":286,\"first_name\":\"Garald\",\"last_name\":\"Park\",\"email\":\"gpark7x@nature.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":14,\"birthday\":\"1999-11-08\"}\n{\"id\":287,\"first_name\":\"Luella\",\"last_name\":\"Stitcher\",\"email\":\"lstitcher7y@bbb.org\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":18,\"birthday\":\"1994-03-01\"}\n{\"id\":288,\"first_name\":\"Rosina\",\"last_name\":\"Matusson\",\"email\":\"rmatusson7z@ox.ac.uk\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":46,\"birthday\":\"1993-06-30\"}\n{\"id\":289,\"first_name\":\"Susanne\",\"last_name\":\"Fredi\",\"email\":\"sfredi80@amazon.co.jp\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":76,\"birthday\":\"1990-08-26\"}\n{\"id\":290,\"first_name\":\"Katherina\",\"last_name\":\"Mobberley\",\"email\":\"kmobberley81@unicef.org\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":35,\"birthday\":\"2001-02-17\"}\n{\"id\":291,\"first_name\":\"Jamal\",\"last_name\":\"Guppey\",\"email\":\"jguppey82@unesco.org\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":41,\"birthday\":\"1997-08-14\"}\n{\"id\":292,\"first_name\":\"Luisa\",\"last_name\":\"Kenworthey\",\"email\":\"lkenworthey83@admin.ch\",\"owns_house\":true,\"favorite_color\":null,\"age\":3,\"birthday\":\"1990-11-05\"}\n{\"id\":293,\"first_name\":\"Balduin\",\"last_name\":\"Siccombe\",\"email\":\"bsiccombe84@list-manage.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":5,\"birthday\":\"1997-01-21\"}\n{\"id\":294,\"first_name\":\"Shay\",\"last_name\":\"Beernt\",\"email\":\"sbeernt85@w3.org\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":36,\"birthday\":\"2003-05-16\"}\n{\"id\":295,\"first_name\":\"Madelle\",\"last_name\":\"Slipper\",\"email\":\"mslipper86@canalblog.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":72,\"birthday\":\"1992-01-19\"}\n{\"id\":296,\"first_name\":\"Edwina\",\"last_name\":\"Halligan\",\"email\":\"ehalligan87@devhub.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":26,\"birthday\":\"1997-02-15\"}\n{\"id\":297,\"first_name\":\"Connie\",\"last_name\":\"Oxlade\",\"email\":\"coxlade88@dell.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":73,\"birthday\":\"1996-12-07\"}\n{\"id\":298,\"first_name\":\"Mandi\",\"last_name\":\"Barthropp\",\"email\":\"mbarthropp89@e-recht24.de\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":16,\"birthday\":\"1990-09-06\"}\n{\"id\":299,\"first_name\":\"Suellen\",\"last_name\":\"Phoenix\",\"email\":\"sphoenix8a@mit.edu\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":78,\"birthday\":\"1999-05-21\"}\n{\"id\":300,\"first_name\":\"Ibbie\",\"last_name\":\"Stayte\",\"email\":\"istayte8b@networkadvertising.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":38,\"birthday\":\"2004-01-13\"}\n{\"id\":301,\"first_name\":\"Kimberley\",\"last_name\":\"Scully\",\"email\":\"kscully8c@last.fm\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":29,\"birthday\":\"1991-08-04\"}\n{\"id\":302,\"first_name\":\"Mitch\",\"last_name\":\"Archdeckne\",\"email\":\"marchdeckne8d@slideshare.net\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":63,\"birthday\":\"2005-09-03\"}\n{\"id\":303,\"first_name\":\"Clevie\",\"last_name\":\"Balazs\",\"email\":\"cbalazs8e@posterous.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":70,\"birthday\":\"2003-05-17\"}\n{\"id\":304,\"first_name\":\"Rochelle\",\"last_name\":\"Morter\",\"email\":\"rmorter8f@squarespace.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":48,\"birthday\":\"2002-01-24\"}\n{\"id\":305,\"first_name\":\"Hy\",\"last_name\":\"Parnham\",\"email\":\"hparnham8g@1und1.de\",\"owns_house\":true,\"favorite_color\":null,\"age\":69,\"birthday\":\"2007-03-19\"}\n{\"id\":306,\"first_name\":\"Dell\",\"last_name\":\"Howorth\",\"email\":\"dhoworth8h@go.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":56,\"birthday\":\"2009-04-04\"}\n{\"id\":307,\"first_name\":\"Inga\",\"last_name\":\"Impey\",\"email\":\"iimpey8i@indiatimes.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":66,\"birthday\":\"2004-10-05\"}\n{\"id\":308,\"first_name\":\"Lillis\",\"last_name\":\"McManamon\",\"email\":\"lmcmanamon8j@state.gov\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":59,\"birthday\":\"1993-06-25\"}\n{\"id\":309,\"first_name\":\"Nickolai\",\"last_name\":\"Hassan\",\"email\":\"nhassan8k@constantcontact.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":42,\"birthday\":\"2005-07-13\"}\n{\"id\":310,\"first_name\":\"Hamish\",\"last_name\":\"Matson\",\"email\":\"hmatson8l@t-online.de\",\"owns_house\":true,\"favorite_color\":null,\"age\":6,\"birthday\":\"1994-09-21\"}\n{\"id\":311,\"first_name\":\"Wittie\",\"last_name\":\"Plaid\",\"email\":\"wplaid8m@w3.org\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":77,\"birthday\":\"2006-04-28\"}\n{\"id\":312,\"first_name\":\"Koo\",\"last_name\":\"Hingeley\",\"email\":\"khingeley8n@yolasite.com\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":66,\"birthday\":\"2005-06-19\"}\n{\"id\":313,\"first_name\":\"Merrel\",\"last_name\":\"Ughetti\",\"email\":\"mughetti8o@apple.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":51,\"birthday\":\"1997-04-17\"}\n{\"id\":314,\"first_name\":\"Marguerite\",\"last_name\":\"Pownall\",\"email\":\"mpownall8p@sakura.ne.jp\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":52,\"birthday\":\"2000-07-09\"}\n{\"id\":315,\"first_name\":\"Boyd\",\"last_name\":\"Dolling\",\"email\":\"bdolling8q@fc2.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":44,\"birthday\":\"1999-11-18\"}\n{\"id\":316,\"first_name\":\"Hale\",\"last_name\":\"Wiltshire\",\"email\":\"hwiltshire8r@sphinn.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":47,\"birthday\":\"1993-09-06\"}\n{\"id\":317,\"first_name\":\"Nicholle\",\"last_name\":\"De Santos\",\"email\":\"ndesantos8s@blogspot.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":38,\"birthday\":\"1996-10-18\"}\n{\"id\":318,\"first_name\":\"Brade\",\"last_name\":\"Studders\",\"email\":\"bstudders8t@aol.com\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":40,\"birthday\":\"1996-10-13\"}\n{\"id\":319,\"first_name\":\"Julie\",\"last_name\":\"Yanukhin\",\"email\":\"jyanukhin8u@youtube.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":28,\"birthday\":\"2006-02-14\"}\n{\"id\":320,\"first_name\":\"Kincaid\",\"last_name\":\"Flaherty\",\"email\":\"kflaherty8v@hibu.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":12,\"birthday\":\"2009-03-30\"}\n{\"id\":321,\"first_name\":\"Blancha\",\"last_name\":\"Pourvoieur\",\"email\":\"bpourvoieur8w@ted.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":66,\"birthday\":\"1997-05-17\"}\n{\"id\":322,\"first_name\":\"Kamilah\",\"last_name\":\"Mulqueeny\",\"email\":\"kmulqueeny8x@stumbleupon.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":46,\"birthday\":\"1990-10-20\"}\n{\"id\":323,\"first_name\":\"Barnebas\",\"last_name\":\"Dannett\",\"email\":\"bdannett8y@about.me\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":41,\"birthday\":\"2005-10-29\"}\n{\"id\":324,\"first_name\":\"Margie\",\"last_name\":\"Paulon\",\"email\":\"mpaulon8z@wikispaces.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":45,\"birthday\":\"1992-04-05\"}\n{\"id\":325,\"first_name\":\"Viv\",\"last_name\":\"Wallbank\",\"email\":\"vwallbank90@linkedin.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":28,\"birthday\":\"2009-05-13\"}\n{\"id\":326,\"first_name\":\"Ambrosius\",\"last_name\":\"Glencrosche\",\"email\":\"aglencrosche91@nytimes.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":69,\"birthday\":\"1997-01-21\"}\n{\"id\":327,\"first_name\":\"Selene\",\"last_name\":\"Goosnell\",\"email\":\"sgoosnell92@wunderground.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":73,\"birthday\":\"1994-10-22\"}\n{\"id\":328,\"first_name\":\"Bent\",\"last_name\":\"Cecere\",\"email\":\"bcecere93@macromedia.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":4,\"birthday\":\"2006-01-31\"}\n{\"id\":329,\"first_name\":\"Johnath\",\"last_name\":\"Ruller\",\"email\":\"jruller94@go.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":25,\"birthday\":\"1990-11-03\"}\n{\"id\":330,\"first_name\":\"Eugenia\",\"last_name\":\"Danielsky\",\"email\":\"edanielsky95@go.com\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":76,\"birthday\":\"2008-09-25\"}\n{\"id\":331,\"first_name\":\"Prinz\",\"last_name\":\"Lawrenceson\",\"email\":\"plawrenceson96@clickbank.net\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":3,\"birthday\":\"1999-01-26\"}\n{\"id\":332,\"first_name\":\"Britteny\",\"last_name\":\"Santer\",\"email\":\"bsanter97@gov.uk\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":14,\"birthday\":\"2009-10-10\"}\n{\"id\":333,\"first_name\":\"Dorette\",\"last_name\":\"Dablin\",\"email\":\"ddablin98@1und1.de\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":35,\"birthday\":\"2005-12-11\"}\n{\"id\":334,\"first_name\":\"Friederike\",\"last_name\":\"Herrero\",\"email\":\"fherrero99@ehow.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":26,\"birthday\":\"1992-11-09\"}\n{\"id\":335,\"first_name\":\"Zora\",\"last_name\":\"Burges\",\"email\":\"zburges9a@storify.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":60,\"birthday\":\"2002-02-19\"}\n{\"id\":336,\"first_name\":\"Hi\",\"last_name\":\"Rosten\",\"email\":\"hrosten9b@behance.net\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":39,\"birthday\":\"2002-08-28\"}\n{\"id\":337,\"first_name\":\"Chloe\",\"last_name\":\"Strase\",\"email\":\"cstrase9c@upenn.edu\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":49,\"birthday\":\"1991-09-02\"}\n{\"id\":338,\"first_name\":\"Bengt\",\"last_name\":\"Graalman\",\"email\":\"bgraalman9d@wikipedia.org\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":42,\"birthday\":\"2003-01-25\"}\n{\"id\":339,\"first_name\":\"Helli\",\"last_name\":\"Massimi\",\"email\":\"hmassimi9e@cyberchimps.com\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":25,\"birthday\":\"2007-04-21\"}\n{\"id\":340,\"first_name\":\"Anallese\",\"last_name\":\"Pitney\",\"email\":\"apitney9f@oaic.gov.au\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":22,\"birthday\":\"1998-09-19\"}\n{\"id\":341,\"first_name\":\"Theodor\",\"last_name\":\"Ulster\",\"email\":\"tulster9g@stumbleupon.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":64,\"birthday\":\"2005-04-18\"}\n{\"id\":342,\"first_name\":\"Ora\",\"last_name\":\"Kenwell\",\"email\":\"okenwell9h@utexas.edu\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":54,\"birthday\":\"1991-02-11\"}\n{\"id\":343,\"first_name\":\"Ardra\",\"last_name\":\"Zylbermann\",\"email\":\"azylbermann9i@hubpages.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":45,\"birthday\":\"2004-08-02\"}\n{\"id\":344,\"first_name\":\"Trefor\",\"last_name\":\"Wilkins\",\"email\":\"twilkins9j@si.edu\",\"owns_house\":true,\"favorite_color\":null,\"age\":51,\"birthday\":\"2008-03-06\"}\n{\"id\":345,\"first_name\":\"Nixie\",\"last_name\":\"Bonin\",\"email\":\"nbonin9k@theatlantic.com\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":25,\"birthday\":\"2000-03-10\"}\n{\"id\":346,\"first_name\":\"Quinta\",\"last_name\":\"Puddephatt\",\"email\":\"qpuddephatt9l@opera.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":11,\"birthday\":\"1993-09-25\"}\n{\"id\":347,\"first_name\":\"Jennee\",\"last_name\":\"Lowrance\",\"email\":\"jlowrance9m@usgs.gov\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":45,\"birthday\":\"1991-08-09\"}\n{\"id\":348,\"first_name\":\"Maura\",\"last_name\":\"Merryweather\",\"email\":\"mmerryweather9n@sohu.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":68,\"birthday\":\"2001-10-01\"}\n{\"id\":349,\"first_name\":\"Dynah\",\"last_name\":\"Gaylord\",\"email\":\"dgaylord9o@technorati.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":61,\"birthday\":\"2005-11-05\"}\n{\"id\":350,\"first_name\":\"Heidi\",\"last_name\":\"Puttrell\",\"email\":\"hputtrell9p@symantec.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":62,\"birthday\":\"2006-11-15\"}\n{\"id\":351,\"first_name\":\"Marga\",\"last_name\":\"Beddoe\",\"email\":\"mbeddoe9q@msu.edu\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":22,\"birthday\":\"2005-09-05\"}\n{\"id\":352,\"first_name\":\"Willey\",\"last_name\":\"Fricker\",\"email\":\"wfricker9r@pagesperso-orange.fr\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":37,\"birthday\":\"1993-07-26\"}\n{\"id\":353,\"first_name\":\"Bibi\",\"last_name\":\"Mudge\",\"email\":\"bmudge9s@cisco.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":3,\"birthday\":\"2008-09-20\"}\n{\"id\":354,\"first_name\":\"Quintana\",\"last_name\":\"Pinar\",\"email\":\"qpinar9t@phoca.cz\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":35,\"birthday\":\"2005-11-01\"}\n{\"id\":355,\"first_name\":\"Reena\",\"last_name\":\"Oneal\",\"email\":\"roneal9u@drupal.org\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":18,\"birthday\":\"2000-11-28\"}\n{\"id\":356,\"first_name\":\"Dorie\",\"last_name\":\"Durrance\",\"email\":\"ddurrance9v@deviantart.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":45,\"birthday\":\"2002-08-09\"}\n{\"id\":357,\"first_name\":\"Carlota\",\"last_name\":\"Whooley\",\"email\":\"cwhooley9w@photobucket.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":45,\"birthday\":\"1990-07-30\"}\n{\"id\":358,\"first_name\":\"Read\",\"last_name\":\"Hast\",\"email\":\"rhast9x@wisc.edu\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":38,\"birthday\":\"1999-09-21\"}\n{\"id\":359,\"first_name\":\"Gottfried\",\"last_name\":\"Perillo\",\"email\":\"gperillo9y@scientificamerican.com\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":41,\"birthday\":\"2000-11-05\"}\n{\"id\":360,\"first_name\":\"Eberto\",\"last_name\":\"Chelsom\",\"email\":\"echelsom9z@slideshare.net\",\"owns_house\":false,\"favorite_color\":null,\"age\":52,\"birthday\":\"2002-10-19\"}\n{\"id\":361,\"first_name\":\"Buckie\",\"last_name\":\"Swinfon\",\"email\":\"bswinfona0@nifty.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":64,\"birthday\":\"2009-04-04\"}\n{\"id\":362,\"first_name\":\"Eilis\",\"last_name\":\"Lawlie\",\"email\":\"elawliea1@bbc.co.uk\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":62,\"birthday\":\"2005-01-25\"}\n{\"id\":363,\"first_name\":\"Jayson\",\"last_name\":\"Greatex\",\"email\":\"jgreatexa2@illinois.edu\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":37,\"birthday\":\"1990-12-28\"}\n{\"id\":364,\"first_name\":\"Annie\",\"last_name\":\"Spowart\",\"email\":\"aspowarta3@wikispaces.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":2,\"birthday\":\"1995-06-27\"}\n{\"id\":365,\"first_name\":\"Mort\",\"last_name\":\"Salamon\",\"email\":\"msalamona4@discuz.net\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":22,\"birthday\":\"1996-03-05\"}\n{\"id\":366,\"first_name\":\"Alfreda\",\"last_name\":\"Maso\",\"email\":\"amasoa5@elpais.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":75,\"birthday\":\"1999-04-15\"}\n{\"id\":367,\"first_name\":\"Erminie\",\"last_name\":\"McLean\",\"email\":\"emcleana6@mlb.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":52,\"birthday\":\"1998-05-02\"}\n{\"id\":368,\"first_name\":\"Astra\",\"last_name\":\"Shynn\",\"email\":\"ashynna7@forbes.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":41,\"birthday\":\"2007-10-29\"}\n{\"id\":369,\"first_name\":\"Ruggiero\",\"last_name\":\"Sissot\",\"email\":\"rsissota8@mysql.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":31,\"birthday\":\"1997-10-25\"}\n{\"id\":370,\"first_name\":\"Jan\",\"last_name\":\"Allsobrook\",\"email\":\"jallsobrooka9@jalbum.net\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":19,\"birthday\":\"1990-07-01\"}\n{\"id\":371,\"first_name\":\"Gannie\",\"last_name\":\"Ruoss\",\"email\":\"gruossaa@google.com.hk\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":74,\"birthday\":\"2001-09-19\"}\n{\"id\":372,\"first_name\":\"Katleen\",\"last_name\":\"Hare\",\"email\":\"khareab@msn.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":36,\"birthday\":\"2003-05-11\"}\n{\"id\":373,\"first_name\":\"Ardella\",\"last_name\":\"Heinritz\",\"email\":\"aheinritzac@patch.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":63,\"birthday\":\"2007-07-21\"}\n{\"id\":374,\"first_name\":\"Evangelina\",\"last_name\":\"Sellen\",\"email\":\"esellenad@newsvine.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":78,\"birthday\":\"2002-04-13\"}\n{\"id\":375,\"first_name\":\"Timmy\",\"last_name\":\"Lammers\",\"email\":\"tlammersae@amazon.de\",\"owns_house\":false,\"favorite_color\":null,\"age\":2,\"birthday\":\"1995-04-25\"}\n{\"id\":376,\"first_name\":\"Nelie\",\"last_name\":\"Coventry\",\"email\":\"ncoventryaf@zimbio.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":77,\"birthday\":\"1998-03-19\"}\n{\"id\":377,\"first_name\":\"Renado\",\"last_name\":\"Robshaw\",\"email\":\"rrobshawag@indiegogo.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":21,\"birthday\":\"2008-04-18\"}\n{\"id\":378,\"first_name\":\"Izzy\",\"last_name\":\"Sewley\",\"email\":\"isewleyah@sogou.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":70,\"birthday\":\"2009-06-29\"}\n{\"id\":379,\"first_name\":\"Timofei\",\"last_name\":\"Probart\",\"email\":\"tprobartai@paypal.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":55,\"birthday\":\"1991-11-10\"}\n{\"id\":380,\"first_name\":\"Merrily\",\"last_name\":\"Pipet\",\"email\":\"mpipetaj@sphinn.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":53,\"birthday\":\"2001-10-24\"}\n{\"id\":381,\"first_name\":\"Raquela\",\"last_name\":\"Enbury\",\"email\":\"renburyak@trellian.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":60,\"birthday\":\"1995-11-30\"}\n{\"id\":382,\"first_name\":\"Aubrey\",\"last_name\":\"Brede\",\"email\":\"abredeal@arizona.edu\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":21,\"birthday\":\"1996-04-14\"}\n{\"id\":383,\"first_name\":\"Gregoor\",\"last_name\":\"Halfacree\",\"email\":\"ghalfacreeam@hud.gov\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":19,\"birthday\":\"1992-07-14\"}\n{\"id\":384,\"first_name\":\"Harley\",\"last_name\":\"Tweedie\",\"email\":\"htweediean@jimdo.com\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":18,\"birthday\":\"2003-03-31\"}\n{\"id\":385,\"first_name\":\"Desiree\",\"last_name\":\"Lucius\",\"email\":\"dluciusao@bloglovin.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":5,\"birthday\":\"1995-06-15\"}\n{\"id\":386,\"first_name\":\"Stanislaw\",\"last_name\":\"Heatherington\",\"email\":\"sheatheringtonap@purevolume.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":24,\"birthday\":\"2008-10-29\"}\n{\"id\":387,\"first_name\":\"Kristen\",\"last_name\":\"Jobbins\",\"email\":\"kjobbinsaq@usda.gov\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":18,\"birthday\":\"2007-04-17\"}\n{\"id\":388,\"first_name\":\"Elisabeth\",\"last_name\":\"Molan\",\"email\":\"emolanar@harvard.edu\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":75,\"birthday\":\"1994-12-11\"}\n{\"id\":389,\"first_name\":\"Dorella\",\"last_name\":\"Jobke\",\"email\":\"djobkeas@epa.gov\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":55,\"birthday\":\"1997-05-05\"}\n{\"id\":390,\"first_name\":\"Minne\",\"last_name\":\"Yakebowitch\",\"email\":\"myakebowitchat@ustream.tv\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":13,\"birthday\":\"2005-06-04\"}\n{\"id\":391,\"first_name\":\"Judon\",\"last_name\":\"Stockey\",\"email\":\"jstockeyau@irs.gov\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":78,\"birthday\":\"2002-12-12\"}\n{\"id\":392,\"first_name\":\"Cecile\",\"last_name\":\"Dunmore\",\"email\":\"cdunmoreav@mashable.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":44,\"birthday\":\"1998-08-03\"}\n{\"id\":393,\"first_name\":\"Uriah\",\"last_name\":\"Reace\",\"email\":\"ureaceaw@so-net.ne.jp\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":36,\"birthday\":\"1994-08-25\"}\n{\"id\":394,\"first_name\":\"Benito\",\"last_name\":\"Lorden\",\"email\":\"blordenax@census.gov\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":75,\"birthday\":\"2000-01-04\"}\n{\"id\":395,\"first_name\":\"Mella\",\"last_name\":\"Kincla\",\"email\":\"mkinclaay@ox.ac.uk\",\"owns_house\":false,\"favorite_color\":null,\"age\":80,\"birthday\":\"2001-06-13\"}\n{\"id\":396,\"first_name\":\"Gil\",\"last_name\":\"MacAdie\",\"email\":\"gmacadieaz@nydailynews.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":32,\"birthday\":\"2005-09-10\"}\n{\"id\":397,\"first_name\":\"Doralin\",\"last_name\":\"Schwaiger\",\"email\":\"dschwaigerb0@mlb.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":31,\"birthday\":\"2002-02-24\"}\n{\"id\":398,\"first_name\":\"Morgen\",\"last_name\":\"Gouldstone\",\"email\":\"mgouldstoneb1@scientificamerican.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":61,\"birthday\":\"1999-08-17\"}\n{\"id\":399,\"first_name\":\"Isidoro\",\"last_name\":\"Davidy\",\"email\":\"idavidyb2@slideshare.net\",\"owns_house\":true,\"favorite_color\":null,\"age\":54,\"birthday\":\"2001-06-01\"}\n{\"id\":400,\"first_name\":\"Elvera\",\"last_name\":\"Gorcke\",\"email\":\"egorckeb3@va.gov\",\"owns_house\":false,\"favorite_color\":null,\"age\":17,\"birthday\":\"2006-08-26\"}\n{\"id\":401,\"first_name\":\"Gelya\",\"last_name\":\"Gurley\",\"email\":\"ggurleyb4@narod.ru\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":36,\"birthday\":\"1990-02-16\"}\n{\"id\":402,\"first_name\":\"Mariya\",\"last_name\":\"Handley\",\"email\":\"mhandleyb5@patch.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":44,\"birthday\":\"1990-08-18\"}\n{\"id\":403,\"first_name\":\"Noland\",\"last_name\":\"Spieck\",\"email\":\"nspieckb6@gizmodo.com\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":13,\"birthday\":\"1991-05-27\"}\n{\"id\":404,\"first_name\":\"Odette\",\"last_name\":\"MacLure\",\"email\":\"omaclureb7@fotki.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":64,\"birthday\":\"2001-08-21\"}\n{\"id\":405,\"first_name\":\"Horten\",\"last_name\":\"Brandrick\",\"email\":\"hbrandrickb8@dagondesign.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":79,\"birthday\":\"2006-02-24\"}\n{\"id\":406,\"first_name\":\"Janifer\",\"last_name\":\"Nulty\",\"email\":\"jnultyb9@comcast.net\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":18,\"birthday\":\"1995-04-13\"}\n{\"id\":407,\"first_name\":\"Cleon\",\"last_name\":\"Krebs\",\"email\":\"ckrebsba@last.fm\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":26,\"birthday\":\"1993-02-13\"}\n{\"id\":408,\"first_name\":\"Gwenore\",\"last_name\":\"Cornill\",\"email\":\"gcornillbb@geocities.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":70,\"birthday\":\"2001-11-06\"}\n{\"id\":409,\"first_name\":\"Car\",\"last_name\":\"Crutchfield\",\"email\":\"ccrutchfieldbc@elpais.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":75,\"birthday\":\"1999-10-28\"}\n{\"id\":410,\"first_name\":\"Jordan\",\"last_name\":\"Carn\",\"email\":\"jcarnbd@gnu.org\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":57,\"birthday\":\"1993-11-02\"}\n{\"id\":411,\"first_name\":\"Chickie\",\"last_name\":\"Karolovsky\",\"email\":\"ckarolovskybe@jiathis.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":44,\"birthday\":\"1997-12-18\"}\n{\"id\":412,\"first_name\":\"Ana\",\"last_name\":\"Mayte\",\"email\":\"amaytebf@360.cn\",\"owns_house\":false,\"favorite_color\":null,\"age\":39,\"birthday\":\"1992-09-09\"}\n{\"id\":413,\"first_name\":\"Leonard\",\"last_name\":\"Jameson\",\"email\":\"ljamesonbg@fastcompany.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":30,\"birthday\":\"2000-04-10\"}\n{\"id\":414,\"first_name\":\"Gustavus\",\"last_name\":\"Reason\",\"email\":\"greasonbh@mozilla.org\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":23,\"birthday\":\"1991-10-23\"}\n{\"id\":415,\"first_name\":\"Gill\",\"last_name\":\"Snowman\",\"email\":\"gsnowmanbi@nba.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":43,\"birthday\":\"2007-08-17\"}\n{\"id\":416,\"first_name\":\"Kippie\",\"last_name\":\"Andrasch\",\"email\":\"kandraschbj@paginegialle.it\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":18,\"birthday\":\"2007-08-10\"}\n{\"id\":417,\"first_name\":\"Moll\",\"last_name\":\"Loyns\",\"email\":\"mloynsbk@over-blog.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":75,\"birthday\":\"1996-02-24\"}\n{\"id\":418,\"first_name\":\"Lyndy\",\"last_name\":\"Gutowska\",\"email\":\"lgutowskabl@fotki.com\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":21,\"birthday\":\"1999-12-24\"}\n{\"id\":419,\"first_name\":\"Frederigo\",\"last_name\":\"Biernacki\",\"email\":\"fbiernackibm@ebay.co.uk\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":2,\"birthday\":\"1999-08-27\"}\n{\"id\":420,\"first_name\":\"Gibb\",\"last_name\":\"Critchard\",\"email\":\"gcritchardbn@webnode.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":58,\"birthday\":\"1999-07-18\"}\n{\"id\":421,\"first_name\":\"Rebecka\",\"last_name\":\"Bagger\",\"email\":\"rbaggerbo@arizona.edu\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":58,\"birthday\":\"2005-12-04\"}\n{\"id\":422,\"first_name\":\"Thorvald\",\"last_name\":\"Jeppe\",\"email\":\"tjeppebp@blogtalkradio.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":40,\"birthday\":\"2009-08-01\"}\n{\"id\":423,\"first_name\":\"Dylan\",\"last_name\":\"End\",\"email\":\"dendbq@howstuffworks.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":46,\"birthday\":\"1995-07-09\"}\n{\"id\":424,\"first_name\":\"Stephanus\",\"last_name\":\"Giovanardi\",\"email\":\"sgiovanardibr@marketwatch.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":14,\"birthday\":\"2006-12-03\"}\n{\"id\":425,\"first_name\":\"Carree\",\"last_name\":\"Jee\",\"email\":\"cjeebs@goodreads.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":51,\"birthday\":\"2009-04-30\"}\n{\"id\":426,\"first_name\":\"Derrick\",\"last_name\":\"Trevarthen\",\"email\":\"dtrevarthenbt@miitbeian.gov.cn\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":27,\"birthday\":\"2005-03-27\"}\n{\"id\":427,\"first_name\":\"Rudie\",\"last_name\":\"Koppes\",\"email\":\"rkoppesbu@intel.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":73,\"birthday\":\"1997-04-25\"}\n{\"id\":428,\"first_name\":\"Adey\",\"last_name\":\"Holah\",\"email\":\"aholahbv@wunderground.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":31,\"birthday\":\"1990-10-21\"}\n{\"id\":429,\"first_name\":\"Devland\",\"last_name\":\"Ezele\",\"email\":\"dezelebw@wix.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":39,\"birthday\":\"1999-10-27\"}\n{\"id\":430,\"first_name\":\"Berrie\",\"last_name\":\"Hyman\",\"email\":\"bhymanbx@ifeng.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":68,\"birthday\":\"1997-01-28\"}\n{\"id\":431,\"first_name\":\"Taite\",\"last_name\":\"Golly\",\"email\":\"tgollyby@ft.com\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":63,\"birthday\":\"1997-08-12\"}\n{\"id\":432,\"first_name\":\"Madelina\",\"last_name\":\"Ivanonko\",\"email\":\"mivanonkobz@list-manage.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":2,\"birthday\":\"1991-07-10\"}\n{\"id\":433,\"first_name\":\"Minnnie\",\"last_name\":\"Paddock\",\"email\":\"mpaddockc0@joomla.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":64,\"birthday\":\"1997-03-30\"}\n{\"id\":434,\"first_name\":\"Vyky\",\"last_name\":\"Phare\",\"email\":\"vpharec1@meetup.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":79,\"birthday\":\"2005-09-03\"}\n{\"id\":435,\"first_name\":\"Garland\",\"last_name\":\"Simonaitis\",\"email\":\"gsimonaitisc2@storify.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":11,\"birthday\":\"2005-06-02\"}\n{\"id\":436,\"first_name\":\"Gilda\",\"last_name\":\"Lathbury\",\"email\":\"glathburyc3@homestead.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":74,\"birthday\":\"2008-12-12\"}\n{\"id\":437,\"first_name\":\"Larisa\",\"last_name\":\"Turpey\",\"email\":\"lturpeyc4@sogou.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":2,\"birthday\":\"1996-05-15\"}\n{\"id\":438,\"first_name\":\"Nicole\",\"last_name\":\"Rounce\",\"email\":\"nrouncec5@devhub.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":61,\"birthday\":\"1994-08-24\"}\n{\"id\":439,\"first_name\":\"Arv\",\"last_name\":\"Dragoe\",\"email\":\"adragoec6@nymag.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":18,\"birthday\":\"2000-10-24\"}\n{\"id\":440,\"first_name\":\"Marlena\",\"last_name\":\"Dowgill\",\"email\":\"mdowgillc7@upenn.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":53,\"birthday\":\"2007-12-31\"}\n{\"id\":441,\"first_name\":\"Terrell\",\"last_name\":\"Renouf\",\"email\":\"trenoufc8@si.edu\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":29,\"birthday\":\"2004-10-10\"}\n{\"id\":442,\"first_name\":\"Collette\",\"last_name\":\"Maypes\",\"email\":\"cmaypesc9@weebly.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":24,\"birthday\":\"2009-03-17\"}\n{\"id\":443,\"first_name\":\"Allyn\",\"last_name\":\"Slowcock\",\"email\":\"aslowcockca@skype.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":10,\"birthday\":\"2009-02-07\"}\n{\"id\":444,\"first_name\":\"Aurora\",\"last_name\":\"Dumbar\",\"email\":\"adumbarcb@unicef.org\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":1,\"birthday\":\"2003-12-09\"}\n{\"id\":445,\"first_name\":\"Rosella\",\"last_name\":\"Jolliss\",\"email\":\"rjollisscc@mit.edu\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":19,\"birthday\":\"2007-10-04\"}\n{\"id\":446,\"first_name\":\"Donelle\",\"last_name\":\"Reggio\",\"email\":\"dreggiocd@hibu.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":1,\"birthday\":\"1995-10-15\"}\n{\"id\":447,\"first_name\":\"Norris\",\"last_name\":\"Symes\",\"email\":\"nsymesce@addtoany.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":30,\"birthday\":\"1994-10-12\"}\n{\"id\":448,\"first_name\":\"Brianne\",\"last_name\":\"Longstaff\",\"email\":\"blongstaffcf@vinaora.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":29,\"birthday\":\"2003-08-04\"}\n{\"id\":449,\"first_name\":\"Pammie\",\"last_name\":\"Lohan\",\"email\":\"plohancg@shutterfly.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":23,\"birthday\":\"2005-03-31\"}\n{\"id\":450,\"first_name\":\"Findley\",\"last_name\":\"Blencoe\",\"email\":\"fblencoech@privacy.gov.au\",\"owns_house\":false,\"favorite_color\":null,\"age\":47,\"birthday\":\"1999-04-29\"}\n{\"id\":451,\"first_name\":\"David\",\"last_name\":\"Ivy\",\"email\":\"divyci@apache.org\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":1,\"birthday\":\"2000-01-03\"}\n{\"id\":452,\"first_name\":\"Giselbert\",\"last_name\":\"Grigori\",\"email\":\"ggrigoricj@ocn.ne.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":19,\"birthday\":\"1991-01-19\"}\n{\"id\":453,\"first_name\":\"Chalmers\",\"last_name\":\"Buick\",\"email\":\"cbuickck@time.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":3,\"birthday\":\"1992-08-23\"}\n{\"id\":454,\"first_name\":\"Taffy\",\"last_name\":\"Grigorini\",\"email\":\"tgrigorinicl@google.it\",\"owns_house\":true,\"favorite_color\":null,\"age\":74,\"birthday\":\"2001-01-19\"}\n{\"id\":455,\"first_name\":\"Agnesse\",\"last_name\":\"Thurlow\",\"email\":\"athurlowcm@nba.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":40,\"birthday\":\"1991-05-16\"}\n{\"id\":456,\"first_name\":\"Fletch\",\"last_name\":\"Solland\",\"email\":\"fsollandcn@friendfeed.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":71,\"birthday\":\"2002-12-12\"}\n{\"id\":457,\"first_name\":\"Nessie\",\"last_name\":\"McLucky\",\"email\":\"nmcluckyco@jigsy.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":62,\"birthday\":\"1990-09-21\"}\n{\"id\":458,\"first_name\":\"Annnora\",\"last_name\":\"Handforth\",\"email\":\"ahandforthcp@macromedia.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":47,\"birthday\":\"2002-10-24\"}\n{\"id\":459,\"first_name\":\"Lorri\",\"last_name\":\"Beatson\",\"email\":\"lbeatsoncq@microsoft.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":73,\"birthday\":\"1998-03-28\"}\n{\"id\":460,\"first_name\":\"Tremayne\",\"last_name\":\"Heijne\",\"email\":\"theijnecr@scientificamerican.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":9,\"birthday\":\"2004-12-17\"}\n{\"id\":461,\"first_name\":\"Dniren\",\"last_name\":\"Ritelli\",\"email\":\"dritellics@i2i.jp\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":28,\"birthday\":\"2004-05-14\"}\n{\"id\":462,\"first_name\":\"Isadore\",\"last_name\":\"Wibrew\",\"email\":\"iwibrewct@ucoz.ru\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":40,\"birthday\":\"1999-10-29\"}\n{\"id\":463,\"first_name\":\"Candis\",\"last_name\":\"Coombe\",\"email\":\"ccoombecu@sohu.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":58,\"birthday\":\"2002-01-10\"}\n{\"id\":464,\"first_name\":\"Clerc\",\"last_name\":\"Spurritt\",\"email\":\"cspurrittcv@microsoft.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":64,\"birthday\":\"2000-10-14\"}\n{\"id\":465,\"first_name\":\"Maxi\",\"last_name\":\"Walworth\",\"email\":\"mwalworthcw@google.de\",\"owns_house\":true,\"favorite_color\":null,\"age\":41,\"birthday\":\"2006-09-03\"}\n{\"id\":466,\"first_name\":\"Dennison\",\"last_name\":\"Alyokhin\",\"email\":\"dalyokhincx@guardian.co.uk\",\"owns_house\":false,\"favorite_color\":null,\"age\":25,\"birthday\":\"1992-12-10\"}\n{\"id\":467,\"first_name\":\"Devondra\",\"last_name\":\"Rhodes\",\"email\":\"drhodescy@usda.gov\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":1,\"birthday\":\"2005-10-14\"}\n{\"id\":468,\"first_name\":\"Barbey\",\"last_name\":\"Guiducci\",\"email\":\"bguiduccicz@feedburner.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":61,\"birthday\":\"1995-10-10\"}\n{\"id\":469,\"first_name\":\"Alida\",\"last_name\":\"Hutchinges\",\"email\":\"ahutchingesd0@privacy.gov.au\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":45,\"birthday\":\"1997-09-15\"}\n{\"id\":470,\"first_name\":\"Xymenes\",\"last_name\":\"Mesant\",\"email\":\"xmesantd1@sohu.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":65,\"birthday\":\"1997-06-04\"}\n{\"id\":471,\"first_name\":\"Giacinta\",\"last_name\":\"Oldcote\",\"email\":\"goldcoted2@pagesperso-orange.fr\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":44,\"birthday\":\"2007-08-14\"}\n{\"id\":472,\"first_name\":\"Vic\",\"last_name\":\"Goodwin\",\"email\":\"vgoodwind3@woothemes.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":7,\"birthday\":\"1992-01-15\"}\n{\"id\":473,\"first_name\":\"Tabatha\",\"last_name\":\"Croose\",\"email\":\"tcroosed4@seesaa.net\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":63,\"birthday\":\"2000-06-08\"}\n{\"id\":474,\"first_name\":\"Joli\",\"last_name\":\"Nowakowska\",\"email\":\"jnowakowskad5@yellowpages.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":9,\"birthday\":\"1990-07-30\"}\n{\"id\":475,\"first_name\":\"Claudius\",\"last_name\":\"Pollicatt\",\"email\":\"cpollicattd6@taobao.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":25,\"birthday\":\"1992-12-15\"}\n{\"id\":476,\"first_name\":\"Jerad\",\"last_name\":\"Spalls\",\"email\":\"jspallsd7@smugmug.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":38,\"birthday\":\"1998-12-20\"}\n{\"id\":477,\"first_name\":\"Katee\",\"last_name\":\"Wohlers\",\"email\":\"kwohlersd8@mapquest.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":11,\"birthday\":\"2002-06-12\"}\n{\"id\":478,\"first_name\":\"Francklin\",\"last_name\":\"McGown\",\"email\":\"fmcgownd9@jugem.jp\",\"owns_house\":true,\"favorite_color\":null,\"age\":39,\"birthday\":\"1997-11-11\"}\n{\"id\":479,\"first_name\":\"Joshia\",\"last_name\":\"Summersby\",\"email\":\"jsummersbyda@dailymail.co.uk\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":43,\"birthday\":\"1998-11-05\"}\n{\"id\":480,\"first_name\":\"Andee\",\"last_name\":\"Coppock.\",\"email\":\"acoppockdb@xing.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":57,\"birthday\":\"2009-04-11\"}\n{\"id\":481,\"first_name\":\"Bambi\",\"last_name\":\"Lothean\",\"email\":\"blotheandc@sciencedirect.com\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":44,\"birthday\":\"1998-11-08\"}\n{\"id\":482,\"first_name\":\"Helenka\",\"last_name\":\"Sheran\",\"email\":\"hsherandd@amazon.de\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":65,\"birthday\":\"1995-06-16\"}\n{\"id\":483,\"first_name\":\"Pavla\",\"last_name\":\"Mityukov\",\"email\":\"pmityukovde@mediafire.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":62,\"birthday\":\"1996-05-03\"}\n{\"id\":484,\"first_name\":\"Judon\",\"last_name\":\"Samwyse\",\"email\":\"jsamwysedf@storify.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":51,\"birthday\":\"2009-10-28\"}\n{\"id\":485,\"first_name\":\"Eugene\",\"last_name\":\"Brient\",\"email\":\"ebrientdg@pcworld.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":75,\"birthday\":\"1998-04-23\"}\n{\"id\":486,\"first_name\":\"Morrie\",\"last_name\":\"Elam\",\"email\":\"melamdh@homestead.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":17,\"birthday\":\"1994-09-14\"}\n{\"id\":487,\"first_name\":\"Bea\",\"last_name\":\"Sellars\",\"email\":\"bsellarsdi@loc.gov\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":63,\"birthday\":\"1992-03-22\"}\n{\"id\":488,\"first_name\":\"Sibyl\",\"last_name\":\"Uwins\",\"email\":\"suwinsdj@amazon.de\",\"owns_house\":true,\"favorite_color\":null,\"age\":32,\"birthday\":\"2007-11-13\"}\n{\"id\":489,\"first_name\":\"Sherwynd\",\"last_name\":\"Anders\",\"email\":\"sandersdk@jigsy.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":1,\"birthday\":\"1996-03-26\"}\n{\"id\":490,\"first_name\":\"Tiler\",\"last_name\":\"Frantzeni\",\"email\":\"tfrantzenidl@columbia.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":24,\"birthday\":\"1995-05-04\"}\n{\"id\":491,\"first_name\":\"Marillin\",\"last_name\":\"Paulet\",\"email\":\"mpauletdm@spotify.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":24,\"birthday\":\"1990-05-02\"}\n{\"id\":492,\"first_name\":\"Daren\",\"last_name\":\"Torritti\",\"email\":\"dtorrittidn@gizmodo.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":69,\"birthday\":\"2007-05-01\"}\n{\"id\":493,\"first_name\":\"Orbadiah\",\"last_name\":\"Hill\",\"email\":\"ohilldo@trellian.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":52,\"birthday\":\"2007-04-12\"}\n{\"id\":494,\"first_name\":\"Matteo\",\"last_name\":\"Herkess\",\"email\":\"mherkessdp@google.nl\",\"owns_house\":false,\"favorite_color\":null,\"age\":13,\"birthday\":\"1999-11-09\"}\n{\"id\":495,\"first_name\":\"Grant\",\"last_name\":\"Geikie\",\"email\":\"ggeikiedq@ft.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":57,\"birthday\":\"1993-02-19\"}\n{\"id\":496,\"first_name\":\"Cece\",\"last_name\":\"Dine-Hart\",\"email\":\"cdinehartdr@nps.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":49,\"birthday\":\"2009-03-28\"}\n{\"id\":497,\"first_name\":\"Boniface\",\"last_name\":\"McKirdy\",\"email\":\"bmckirdyds@live.com\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":80,\"birthday\":\"1998-07-12\"}\n{\"id\":498,\"first_name\":\"Sabra\",\"last_name\":\"Loadsman\",\"email\":\"sloadsmandt@ning.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":45,\"birthday\":\"2002-12-04\"}\n{\"id\":499,\"first_name\":\"Emlyn\",\"last_name\":\"Askem\",\"email\":\"easkemdu@weather.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":3,\"birthday\":\"1996-09-01\"}\n{\"id\":500,\"first_name\":\"Cosetta\",\"last_name\":\"Smitton\",\"email\":\"csmittondv@fema.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":77,\"birthday\":\"2006-06-01\"}\n{\"id\":501,\"first_name\":\"Sawyere\",\"last_name\":\"Bensley\",\"email\":\"sbensleydw@w3.org\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":56,\"birthday\":\"2004-03-12\"}\n{\"id\":502,\"first_name\":\"Kellby\",\"last_name\":\"Trainor\",\"email\":\"ktrainordx@tinyurl.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":46,\"birthday\":\"1990-06-18\"}\n{\"id\":503,\"first_name\":\"Tamarra\",\"last_name\":\"Secker\",\"email\":\"tseckerdy@creativecommons.org\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":20,\"birthday\":\"2000-01-23\"}\n{\"id\":504,\"first_name\":\"Dwayne\",\"last_name\":\"Vasilov\",\"email\":\"dvasilovdz@sakura.ne.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":64,\"birthday\":\"1999-02-12\"}\n{\"id\":505,\"first_name\":\"Guglielmo\",\"last_name\":\"Cavill\",\"email\":\"gcaville0@about.me\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":45,\"birthday\":\"1991-11-27\"}\n{\"id\":506,\"first_name\":\"Gerti\",\"last_name\":\"Gillow\",\"email\":\"ggillowe1@canalblog.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":30,\"birthday\":\"2009-09-27\"}\n{\"id\":507,\"first_name\":\"Aube\",\"last_name\":\"Richardon\",\"email\":\"arichardone2@lycos.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":68,\"birthday\":\"2002-01-09\"}\n{\"id\":508,\"first_name\":\"Roy\",\"last_name\":\"Petruska\",\"email\":\"rpetruskae3@ebay.co.uk\",\"owns_house\":false,\"favorite_color\":null,\"age\":77,\"birthday\":\"2000-04-26\"}\n{\"id\":509,\"first_name\":\"Jorry\",\"last_name\":\"Leversuch\",\"email\":\"jleversuche4@histats.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":28,\"birthday\":\"1994-03-22\"}\n{\"id\":510,\"first_name\":\"Halsey\",\"last_name\":\"Seivertsen\",\"email\":\"hseivertsene5@craigslist.org\",\"owns_house\":true,\"favorite_color\":null,\"age\":11,\"birthday\":\"1997-02-20\"}\n{\"id\":511,\"first_name\":\"Alvina\",\"last_name\":\"Marlor\",\"email\":\"amarlore6@ezinearticles.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":33,\"birthday\":\"2004-03-06\"}\n{\"id\":512,\"first_name\":\"Bevin\",\"last_name\":\"Sumpter\",\"email\":\"bsumptere7@com.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":80,\"birthday\":\"2000-12-12\"}\n{\"id\":513,\"first_name\":\"Domenico\",\"last_name\":\"Claybourne\",\"email\":\"dclaybournee8@microsoft.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":46,\"birthday\":\"1990-09-10\"}\n{\"id\":514,\"first_name\":\"Asher\",\"last_name\":\"Silliman\",\"email\":\"asillimane9@instagram.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":8,\"birthday\":\"2007-01-24\"}\n{\"id\":515,\"first_name\":\"Morgun\",\"last_name\":\"Shafe\",\"email\":\"mshafeea@t.co\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":64,\"birthday\":\"1991-05-13\"}\n{\"id\":516,\"first_name\":\"Christos\",\"last_name\":\"Dymond\",\"email\":\"cdymondeb@nytimes.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":58,\"birthday\":\"2007-07-21\"}\n{\"id\":517,\"first_name\":\"Phoebe\",\"last_name\":\"Feild\",\"email\":\"pfeildec@comsenz.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":22,\"birthday\":\"1993-06-18\"}\n{\"id\":518,\"first_name\":\"Rodney\",\"last_name\":\"Ladlow\",\"email\":\"rladlowed@creativecommons.org\",\"owns_house\":true,\"favorite_color\":null,\"age\":63,\"birthday\":\"1999-08-11\"}\n{\"id\":519,\"first_name\":\"Leopold\",\"last_name\":\"Betteson\",\"email\":\"lbettesonee@bizjournals.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":60,\"birthday\":\"2009-04-22\"}\n{\"id\":520,\"first_name\":\"Rudiger\",\"last_name\":\"Suston\",\"email\":\"rsustonef@google.de\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":52,\"birthday\":\"1990-08-08\"}\n{\"id\":521,\"first_name\":\"Cary\",\"last_name\":\"Main\",\"email\":\"cmaineg@unblog.fr\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":42,\"birthday\":\"1991-08-23\"}\n{\"id\":522,\"first_name\":\"Von\",\"last_name\":\"Byrkmyr\",\"email\":\"vbyrkmyreh@acquirethisname.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":53,\"birthday\":\"2008-01-21\"}\n{\"id\":523,\"first_name\":\"Karry\",\"last_name\":\"Redsell\",\"email\":\"kredsellei@youtube.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":51,\"birthday\":\"2006-05-09\"}\n{\"id\":524,\"first_name\":\"Mikol\",\"last_name\":\"Mattei\",\"email\":\"mmatteiej@chron.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":47,\"birthday\":\"1999-05-31\"}\n{\"id\":525,\"first_name\":\"Dona\",\"last_name\":\"Biggerstaff\",\"email\":\"dbiggerstaffek@tripadvisor.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":50,\"birthday\":\"1994-04-05\"}\n{\"id\":526,\"first_name\":\"Carmon\",\"last_name\":\"Proven\",\"email\":\"cprovenel@hostgator.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":51,\"birthday\":\"2009-10-16\"}\n{\"id\":527,\"first_name\":\"Ameline\",\"last_name\":\"Lawson\",\"email\":\"alawsonem@icio.us\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":76,\"birthday\":\"2001-04-10\"}\n{\"id\":528,\"first_name\":\"Lorrayne\",\"last_name\":\"Daouze\",\"email\":\"ldaouzeen@wp.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":26,\"birthday\":\"2007-01-30\"}\n{\"id\":529,\"first_name\":\"Jase\",\"last_name\":\"Jessope\",\"email\":\"jjessopeeo@list-manage.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":42,\"birthday\":\"1996-10-25\"}\n{\"id\":530,\"first_name\":\"Rea\",\"last_name\":\"Spowart\",\"email\":\"rspowartep@desdev.cn\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":70,\"birthday\":\"1990-05-02\"}\n{\"id\":531,\"first_name\":\"Luce\",\"last_name\":\"Lowis\",\"email\":\"llowiseq@jimdo.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":28,\"birthday\":\"2006-10-19\"}\n{\"id\":532,\"first_name\":\"Seana\",\"last_name\":\"Insko\",\"email\":\"sinskoer@t-online.de\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":80,\"birthday\":\"2004-08-07\"}\n{\"id\":533,\"first_name\":\"Domenic\",\"last_name\":\"Arkwright\",\"email\":\"darkwrightes@weibo.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":35,\"birthday\":\"2000-04-05\"}\n{\"id\":534,\"first_name\":\"Gun\",\"last_name\":\"Cahn\",\"email\":\"gcahnet@vistaprint.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":32,\"birthday\":\"2004-02-12\"}\n{\"id\":535,\"first_name\":\"Kev\",\"last_name\":\"Lukianovich\",\"email\":\"klukianovicheu@mtv.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":76,\"birthday\":\"1991-08-01\"}\n{\"id\":536,\"first_name\":\"Archy\",\"last_name\":\"McClosh\",\"email\":\"amccloshev@yellowbook.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":27,\"birthday\":\"2009-09-28\"}\n{\"id\":537,\"first_name\":\"Ailee\",\"last_name\":\"Goulding\",\"email\":\"agouldingew@vistaprint.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":61,\"birthday\":\"1996-02-11\"}\n{\"id\":538,\"first_name\":\"Chiquia\",\"last_name\":\"Postill\",\"email\":\"cpostillex@nydailynews.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":42,\"birthday\":\"1998-07-18\"}\n{\"id\":539,\"first_name\":\"Amalea\",\"last_name\":\"Winterflood\",\"email\":\"awinterfloodey@seesaa.net\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":48,\"birthday\":\"2008-08-14\"}\n{\"id\":540,\"first_name\":\"Irwinn\",\"last_name\":\"Southwell\",\"email\":\"isouthwellez@epa.gov\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":54,\"birthday\":\"1998-08-07\"}\n{\"id\":541,\"first_name\":\"Juliana\",\"last_name\":\"Wall\",\"email\":\"jwallf0@oaic.gov.au\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1999-01-04\"}\n{\"id\":542,\"first_name\":\"Damon\",\"last_name\":\"Henriksson\",\"email\":\"dhenrikssonf1@topsy.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":48,\"birthday\":\"2004-06-26\"}\n{\"id\":543,\"first_name\":\"Shayna\",\"last_name\":\"Andreev\",\"email\":\"sandreevf2@blogspot.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":16,\"birthday\":\"1992-07-03\"}\n{\"id\":544,\"first_name\":\"Sheryl\",\"last_name\":\"Gregorin\",\"email\":\"sgregorinf3@pagesperso-orange.fr\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":39,\"birthday\":\"2009-09-03\"}\n{\"id\":545,\"first_name\":\"Sula\",\"last_name\":\"Peller\",\"email\":\"spellerf4@dagondesign.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":28,\"birthday\":\"2001-03-10\"}\n{\"id\":546,\"first_name\":\"Melosa\",\"last_name\":\"Grunnell\",\"email\":\"mgrunnellf5@salon.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":67,\"birthday\":\"2007-10-07\"}\n{\"id\":547,\"first_name\":\"Bernetta\",\"last_name\":\"Burkett\",\"email\":\"bburkettf6@sun.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":5,\"birthday\":\"1992-01-16\"}\n{\"id\":548,\"first_name\":\"Herculie\",\"last_name\":\"Stower\",\"email\":\"hstowerf7@jalbum.net\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":41,\"birthday\":\"1992-09-03\"}\n{\"id\":549,\"first_name\":\"Roosevelt\",\"last_name\":\"Ecob\",\"email\":\"recobf8@so-net.ne.jp\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":50,\"birthday\":\"1998-11-20\"}\n{\"id\":550,\"first_name\":\"Judd\",\"last_name\":\"Royse\",\"email\":\"jroysef9@archive.org\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":11,\"birthday\":\"2006-07-12\"}\n{\"id\":551,\"first_name\":\"Else\",\"last_name\":\"Vergine\",\"email\":\"everginefa@statcounter.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":34,\"birthday\":\"2007-04-01\"}\n{\"id\":552,\"first_name\":\"Benedikta\",\"last_name\":\"Bernasek\",\"email\":\"bbernasekfb@bravesites.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":39,\"birthday\":\"1996-04-04\"}\n{\"id\":553,\"first_name\":\"Tirrell\",\"last_name\":\"Gottelier\",\"email\":\"tgottelierfc@hibu.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":59,\"birthday\":\"1992-07-26\"}\n{\"id\":554,\"first_name\":\"Persis\",\"last_name\":\"Malicki\",\"email\":\"pmalickifd@time.com\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":78,\"birthday\":\"2000-12-30\"}\n{\"id\":555,\"first_name\":\"Toddie\",\"last_name\":\"Brignell\",\"email\":\"tbrignellfe@deliciousdays.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":55,\"birthday\":\"2007-10-19\"}\n{\"id\":556,\"first_name\":\"Gelya\",\"last_name\":\"Tidmarsh\",\"email\":\"gtidmarshff@statcounter.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":40,\"birthday\":\"1990-11-25\"}\n{\"id\":557,\"first_name\":\"Law\",\"last_name\":\"Soutter\",\"email\":\"lsoutterfg@comcast.net\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":24,\"birthday\":\"1996-05-23\"}\n{\"id\":558,\"first_name\":\"Wynne\",\"last_name\":\"Folan\",\"email\":\"wfolanfh@cnet.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":60,\"birthday\":\"2008-10-29\"}\n{\"id\":559,\"first_name\":\"Gil\",\"last_name\":\"Petticrow\",\"email\":\"gpetticrowfi@bigcartel.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":41,\"birthday\":\"2003-06-02\"}\n{\"id\":560,\"first_name\":\"Johann\",\"last_name\":\"Mahaddy\",\"email\":\"jmahaddyfj@uiuc.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":8,\"birthday\":\"2005-12-05\"}\n{\"id\":561,\"first_name\":\"Orland\",\"last_name\":\"Penson\",\"email\":\"opensonfk@dyndns.org\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":14,\"birthday\":\"2009-09-16\"}\n{\"id\":562,\"first_name\":\"Myrlene\",\"last_name\":\"Avrahamy\",\"email\":\"mavrahamyfl@ihg.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":30,\"birthday\":\"2000-04-06\"}\n{\"id\":563,\"first_name\":\"Rhodia\",\"last_name\":\"Duckers\",\"email\":\"rduckersfm@reuters.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":17,\"birthday\":\"2001-03-09\"}\n{\"id\":564,\"first_name\":\"Modesta\",\"last_name\":\"Barbrick\",\"email\":\"mbarbrickfn@ezinearticles.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":52,\"birthday\":\"1997-05-12\"}\n{\"id\":565,\"first_name\":\"Gregorius\",\"last_name\":\"Tarrant\",\"email\":\"gtarrantfo@pinterest.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":40,\"birthday\":\"2001-07-03\"}\n{\"id\":566,\"first_name\":\"Reginauld\",\"last_name\":\"Fraine\",\"email\":\"rfrainefp@statcounter.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":54,\"birthday\":\"2001-11-02\"}\n{\"id\":567,\"first_name\":\"Jerad\",\"last_name\":\"Walne\",\"email\":\"jwalnefq@nytimes.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":29,\"birthday\":\"1994-11-23\"}\n{\"id\":568,\"first_name\":\"Dicky\",\"last_name\":\"Sincock\",\"email\":\"dsincockfr@topsy.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":79,\"birthday\":\"2002-08-15\"}\n{\"id\":569,\"first_name\":\"Chickie\",\"last_name\":\"Bottrill\",\"email\":\"cbottrillfs@theglobeandmail.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":18,\"birthday\":\"1991-12-17\"}\n{\"id\":570,\"first_name\":\"Augusto\",\"last_name\":\"Piscopello\",\"email\":\"apiscopelloft@cam.ac.uk\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":41,\"birthday\":\"1992-03-03\"}\n{\"id\":571,\"first_name\":\"Thoma\",\"last_name\":\"Verbrugghen\",\"email\":\"tverbrugghenfu@purevolume.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":22,\"birthday\":\"1998-04-10\"}\n{\"id\":572,\"first_name\":\"Jason\",\"last_name\":\"Sodeau\",\"email\":\"jsodeaufv@google.co.jp\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":2,\"birthday\":\"1990-09-09\"}\n{\"id\":573,\"first_name\":\"Gabbi\",\"last_name\":\"Carous\",\"email\":\"gcarousfw@rambler.ru\",\"owns_house\":true,\"favorite_color\":null,\"age\":24,\"birthday\":\"1992-03-15\"}\n{\"id\":574,\"first_name\":\"Dawn\",\"last_name\":\"Portal\",\"email\":\"dportalfx@nifty.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":80,\"birthday\":\"1995-02-23\"}\n{\"id\":575,\"first_name\":\"Calypso\",\"last_name\":\"McDaid\",\"email\":\"cmcdaidfy@godaddy.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":29,\"birthday\":\"2005-08-30\"}\n{\"id\":576,\"first_name\":\"Easter\",\"last_name\":\"Thumnel\",\"email\":\"ethumnelfz@rediff.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":13,\"birthday\":\"1994-06-19\"}\n{\"id\":577,\"first_name\":\"Georgine\",\"last_name\":\"Gleader\",\"email\":\"ggleaderg0@unblog.fr\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":39,\"birthday\":\"1999-06-20\"}\n{\"id\":578,\"first_name\":\"Horatio\",\"last_name\":\"McLorinan\",\"email\":\"hmclorinang1@forbes.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":41,\"birthday\":\"2005-05-11\"}\n{\"id\":579,\"first_name\":\"Irma\",\"last_name\":\"Laundon\",\"email\":\"ilaundong2@guardian.co.uk\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":44,\"birthday\":\"2007-04-29\"}\n{\"id\":580,\"first_name\":\"Elmer\",\"last_name\":\"Adamov\",\"email\":\"eadamovg3@istockphoto.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":8,\"birthday\":\"1997-09-06\"}\n{\"id\":581,\"first_name\":\"Kirsteni\",\"last_name\":\"Di Maria\",\"email\":\"kdimariag4@webmd.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":69,\"birthday\":\"1992-02-19\"}\n{\"id\":582,\"first_name\":\"Janek\",\"last_name\":\"Cours\",\"email\":\"jcoursg5@t-online.de\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":69,\"birthday\":\"1991-07-30\"}\n{\"id\":583,\"first_name\":\"Lana\",\"last_name\":\"Doxey\",\"email\":\"ldoxeyg6@quantcast.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":17,\"birthday\":\"1993-05-21\"}\n{\"id\":584,\"first_name\":\"Rosanna\",\"last_name\":\"Cayle\",\"email\":\"rcayleg7@networkadvertising.org\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":73,\"birthday\":\"1996-06-27\"}\n{\"id\":585,\"first_name\":\"Sonny\",\"last_name\":\"Dunnaway\",\"email\":\"sdunnawayg8@hatena.ne.jp\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":38,\"birthday\":\"2003-02-18\"}\n{\"id\":586,\"first_name\":\"Ade\",\"last_name\":\"MacMaster\",\"email\":\"amacmasterg9@vistaprint.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":5,\"birthday\":\"1990-05-21\"}\n{\"id\":587,\"first_name\":\"Jamey\",\"last_name\":\"Gartell\",\"email\":\"jgartellga@ftc.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":65,\"birthday\":\"2001-05-21\"}\n{\"id\":588,\"first_name\":\"Llewellyn\",\"last_name\":\"Powland\",\"email\":\"lpowlandgb@gmpg.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":63,\"birthday\":\"2003-02-28\"}\n{\"id\":589,\"first_name\":\"Normie\",\"last_name\":\"Empleton\",\"email\":\"nempletongc@parallels.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":47,\"birthday\":\"2006-03-26\"}\n{\"id\":590,\"first_name\":\"Care\",\"last_name\":\"Doerffer\",\"email\":\"cdoerffergd@craigslist.org\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":29,\"birthday\":\"2000-08-28\"}\n{\"id\":591,\"first_name\":\"Sianna\",\"last_name\":\"O'Neary\",\"email\":\"sonearyge@redcross.org\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":5,\"birthday\":\"2003-08-04\"}\n{\"id\":592,\"first_name\":\"Reagen\",\"last_name\":\"McQuarrie\",\"email\":\"rmcquarriegf@apache.org\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":10,\"birthday\":\"1990-05-20\"}\n{\"id\":593,\"first_name\":\"Hermy\",\"last_name\":\"Denys\",\"email\":\"hdenysgg@wsj.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":43,\"birthday\":\"1995-06-24\"}\n{\"id\":594,\"first_name\":\"Editha\",\"last_name\":\"Gerrie\",\"email\":\"egerriegh@example.com\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":53,\"birthday\":\"1991-12-08\"}\n{\"id\":595,\"first_name\":\"Junette\",\"last_name\":\"Jedrzejewsky\",\"email\":\"jjedrzejewskygi@devhub.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":24,\"birthday\":\"1994-01-12\"}\n{\"id\":596,\"first_name\":\"Boony\",\"last_name\":\"Jarmaine\",\"email\":\"bjarmainegj@instagram.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":12,\"birthday\":\"1992-08-19\"}\n{\"id\":597,\"first_name\":\"Miltie\",\"last_name\":\"Greep\",\"email\":\"mgreepgk@nps.gov\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":61,\"birthday\":\"2002-08-23\"}\n{\"id\":598,\"first_name\":\"Denys\",\"last_name\":\"Acey\",\"email\":\"daceygl@acquirethisname.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":3,\"birthday\":\"2005-03-10\"}\n{\"id\":599,\"first_name\":\"Carlyle\",\"last_name\":\"Lockton\",\"email\":\"clocktongm@theguardian.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":33,\"birthday\":\"1994-07-19\"}\n{\"id\":600,\"first_name\":\"Issi\",\"last_name\":\"Reisen\",\"email\":\"ireisengn@chron.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":50,\"birthday\":\"2001-03-17\"}\n{\"id\":601,\"first_name\":\"Charlot\",\"last_name\":\"Vinson\",\"email\":\"cvinsongo@youku.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":80,\"birthday\":\"2007-07-24\"}\n{\"id\":602,\"first_name\":\"Petr\",\"last_name\":\"Dmitrovic\",\"email\":\"pdmitrovicgp@yellowbook.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":70,\"birthday\":\"2005-05-04\"}\n{\"id\":603,\"first_name\":\"Jolyn\",\"last_name\":\"Swindle\",\"email\":\"jswindlegq@eventbrite.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":26,\"birthday\":\"2008-08-29\"}\n{\"id\":604,\"first_name\":\"Sherill\",\"last_name\":\"Seth\",\"email\":\"ssethgr@eventbrite.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":4,\"birthday\":\"2003-09-20\"}\n{\"id\":605,\"first_name\":\"Marita\",\"last_name\":\"Loffill\",\"email\":\"mloffillgs@economist.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":54,\"birthday\":\"1990-11-01\"}\n{\"id\":606,\"first_name\":\"Koo\",\"last_name\":\"Worham\",\"email\":\"kworhamgt@statcounter.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":17,\"birthday\":\"2001-10-15\"}\n{\"id\":607,\"first_name\":\"Shanta\",\"last_name\":\"Delouch\",\"email\":\"sdelouchgu@nifty.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":43,\"birthday\":\"1998-12-25\"}\n{\"id\":608,\"first_name\":\"Alysia\",\"last_name\":\"Spurdon\",\"email\":\"aspurdongv@wsj.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":50,\"birthday\":\"1994-11-28\"}\n{\"id\":609,\"first_name\":\"Jenny\",\"last_name\":\"Wheater\",\"email\":\"jwheatergw@hugedomains.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":4,\"birthday\":\"2000-12-24\"}\n{\"id\":610,\"first_name\":\"Michell\",\"last_name\":\"Yurkevich\",\"email\":\"myurkevichgx@ox.ac.uk\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":75,\"birthday\":\"2008-06-17\"}\n{\"id\":611,\"first_name\":\"Annmaria\",\"last_name\":\"MacKniely\",\"email\":\"amacknielygy@yale.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":42,\"birthday\":\"2005-11-17\"}\n{\"id\":612,\"first_name\":\"Francklin\",\"last_name\":\"Le Brum\",\"email\":\"flebrumgz@answers.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":26,\"birthday\":\"1996-12-15\"}\n{\"id\":613,\"first_name\":\"Hyacinthe\",\"last_name\":\"Perryman\",\"email\":\"hperrymanh0@ucoz.ru\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":14,\"birthday\":\"1995-04-16\"}\n{\"id\":614,\"first_name\":\"Sari\",\"last_name\":\"Falvey\",\"email\":\"sfalveyh1@mozilla.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":67,\"birthday\":\"1994-03-05\"}\n{\"id\":615,\"first_name\":\"Geordie\",\"last_name\":\"Nockalls\",\"email\":\"gnockallsh2@odnoklassniki.ru\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":59,\"birthday\":\"1991-07-18\"}\n{\"id\":616,\"first_name\":\"Jourdan\",\"last_name\":\"Pitts\",\"email\":\"jpittsh3@chron.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":63,\"birthday\":\"2003-04-29\"}\n{\"id\":617,\"first_name\":\"Joeann\",\"last_name\":\"Dymoke\",\"email\":\"jdymokeh4@clickbank.net\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":78,\"birthday\":\"2007-12-31\"}\n{\"id\":618,\"first_name\":\"Pamella\",\"last_name\":\"Eary\",\"email\":\"pearyh5@buzzfeed.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":45,\"birthday\":\"2009-12-18\"}\n{\"id\":619,\"first_name\":\"Karie\",\"last_name\":\"Fleming\",\"email\":\"kflemingh6@unicef.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":63,\"birthday\":\"1991-10-31\"}\n{\"id\":620,\"first_name\":\"Rubetta\",\"last_name\":\"Dargan\",\"email\":\"rdarganh7@prweb.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":80,\"birthday\":\"2002-03-05\"}\n{\"id\":621,\"first_name\":\"Kylen\",\"last_name\":\"Dionisii\",\"email\":\"kdionisiih8@squarespace.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"2009-02-15\"}\n{\"id\":622,\"first_name\":\"Jasen\",\"last_name\":\"Abotson\",\"email\":\"jabotsonh9@yale.edu\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":22,\"birthday\":\"1992-03-23\"}\n{\"id\":623,\"first_name\":\"Gwynne\",\"last_name\":\"McCole\",\"email\":\"gmccoleha@hubpages.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":70,\"birthday\":\"2001-12-24\"}\n{\"id\":624,\"first_name\":\"Leanna\",\"last_name\":\"Boyse\",\"email\":\"lboysehb@eventbrite.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":40,\"birthday\":\"1991-10-21\"}\n{\"id\":625,\"first_name\":\"Claudia\",\"last_name\":\"Schutze\",\"email\":\"cschutzehc@wufoo.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":33,\"birthday\":\"2004-03-13\"}\n{\"id\":626,\"first_name\":\"Kelsy\",\"last_name\":\"Moyles\",\"email\":\"kmoyleshd@unc.edu\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":42,\"birthday\":\"1991-12-13\"}\n{\"id\":627,\"first_name\":\"Erhard\",\"last_name\":\"Delaprelle\",\"email\":\"edelaprellehe@cornell.edu\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":56,\"birthday\":\"2003-08-15\"}\n{\"id\":628,\"first_name\":\"Barbara\",\"last_name\":\"Bainbridge\",\"email\":\"bbainbridgehf@opera.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":21,\"birthday\":\"1998-12-04\"}\n{\"id\":629,\"first_name\":\"Shirlee\",\"last_name\":\"Fante\",\"email\":\"sfantehg@xinhuanet.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":40,\"birthday\":\"2009-10-16\"}\n{\"id\":630,\"first_name\":\"Kinsley\",\"last_name\":\"Krolle\",\"email\":\"kkrollehh@about.me\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":38,\"birthday\":\"1991-12-11\"}\n{\"id\":631,\"first_name\":\"Shaun\",\"last_name\":\"Lavielle\",\"email\":\"slaviellehi@about.me\",\"owns_house\":false,\"favorite_color\":null,\"age\":34,\"birthday\":\"1993-07-15\"}\n{\"id\":632,\"first_name\":\"Mabelle\",\"last_name\":\"Griston\",\"email\":\"mgristonhj@cloudflare.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":38,\"birthday\":\"2008-10-20\"}\n{\"id\":633,\"first_name\":\"Vasili\",\"last_name\":\"Rigolle\",\"email\":\"vrigollehk@jimdo.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":71,\"birthday\":\"1996-03-22\"}\n{\"id\":634,\"first_name\":\"Malchy\",\"last_name\":\"Spancock\",\"email\":\"mspancockhl@opensource.org\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":11,\"birthday\":\"2002-12-28\"}\n{\"id\":635,\"first_name\":\"Zilvia\",\"last_name\":\"Ware\",\"email\":\"zwarehm@addtoany.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":25,\"birthday\":\"2001-04-03\"}\n{\"id\":636,\"first_name\":\"Hilary\",\"last_name\":\"Jorge\",\"email\":\"hjorgehn@sina.com.cn\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":7,\"birthday\":\"2007-07-24\"}\n{\"id\":637,\"first_name\":\"Nicol\",\"last_name\":\"Vile\",\"email\":\"nvileho@businessinsider.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":60,\"birthday\":\"1992-12-27\"}\n{\"id\":638,\"first_name\":\"Cherilynn\",\"last_name\":\"Upwood\",\"email\":\"cupwoodhp@hao123.com\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":33,\"birthday\":\"1990-06-10\"}\n{\"id\":639,\"first_name\":\"Sholom\",\"last_name\":\"Kolis\",\"email\":\"skolishq@wsj.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":78,\"birthday\":\"1997-09-07\"}\n{\"id\":640,\"first_name\":\"Nicki\",\"last_name\":\"McIlvenna\",\"email\":\"nmcilvennahr@oakley.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":55,\"birthday\":\"2003-11-04\"}\n{\"id\":641,\"first_name\":\"Butch\",\"last_name\":\"Ripsher\",\"email\":\"bripsherhs@zdnet.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":63,\"birthday\":\"2002-04-28\"}\n{\"id\":642,\"first_name\":\"Gill\",\"last_name\":\"Poppleton\",\"email\":\"gpoppletonht@wp.com\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":16,\"birthday\":\"1999-10-13\"}\n{\"id\":643,\"first_name\":\"Marigold\",\"last_name\":\"Van Vuuren\",\"email\":\"mvanvuurenhu@soup.io\",\"owns_house\":true,\"favorite_color\":null,\"age\":4,\"birthday\":\"1998-09-13\"}\n{\"id\":644,\"first_name\":\"Parnell\",\"last_name\":\"Teece\",\"email\":\"pteecehv@reverbnation.com\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":22,\"birthday\":\"2007-01-23\"}\n{\"id\":645,\"first_name\":\"Jereme\",\"last_name\":\"Bainbridge\",\"email\":\"jbainbridgehw@cdc.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":79,\"birthday\":\"1994-05-26\"}\n{\"id\":646,\"first_name\":\"Ernaline\",\"last_name\":\"Haddington\",\"email\":\"ehaddingtonhx@about.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":74,\"birthday\":\"2008-02-16\"}\n{\"id\":647,\"first_name\":\"Maye\",\"last_name\":\"Bergen\",\"email\":\"mbergenhy@flickr.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":75,\"birthday\":\"2001-05-06\"}\n{\"id\":648,\"first_name\":\"Esra\",\"last_name\":\"Faivre\",\"email\":\"efaivrehz@ustream.tv\",\"owns_house\":false,\"favorite_color\":null,\"age\":25,\"birthday\":\"1994-05-28\"}\n{\"id\":649,\"first_name\":\"Gwyneth\",\"last_name\":\"Vanlint\",\"email\":\"gvanlinti0@soup.io\",\"owns_house\":false,\"favorite_color\":null,\"age\":52,\"birthday\":\"1998-07-18\"}\n{\"id\":650,\"first_name\":\"Woodman\",\"last_name\":\"Kilner\",\"email\":\"wkilneri1@smugmug.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":14,\"birthday\":\"1994-04-25\"}\n{\"id\":651,\"first_name\":\"Dyna\",\"last_name\":\"Milier\",\"email\":\"dmilieri2@lulu.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":40,\"birthday\":\"1996-08-23\"}\n{\"id\":652,\"first_name\":\"Barbara\",\"last_name\":\"Baldassi\",\"email\":\"bbaldassii3@apple.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":36,\"birthday\":\"2006-09-07\"}\n{\"id\":653,\"first_name\":\"Asher\",\"last_name\":\"Bulloch\",\"email\":\"abullochi4@theatlantic.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":5,\"birthday\":\"1996-10-21\"}\n{\"id\":654,\"first_name\":\"Valentine\",\"last_name\":\"Browett\",\"email\":\"vbrowetti5@dyndns.org\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":54,\"birthday\":\"2007-08-07\"}\n{\"id\":655,\"first_name\":\"Carlota\",\"last_name\":\"Keppe\",\"email\":\"ckeppei6@taobao.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":74,\"birthday\":\"2001-09-12\"}\n{\"id\":656,\"first_name\":\"Nefen\",\"last_name\":\"Hasnney\",\"email\":\"nhasnneyi7@google.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":7,\"birthday\":\"1991-11-20\"}\n{\"id\":657,\"first_name\":\"Leyla\",\"last_name\":\"Fardoe\",\"email\":\"lfardoei8@sourceforge.net\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":60,\"birthday\":\"1998-02-06\"}\n{\"id\":658,\"first_name\":\"Tye\",\"last_name\":\"Klisch\",\"email\":\"tklischi9@yale.edu\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":63,\"birthday\":\"1992-10-23\"}\n{\"id\":659,\"first_name\":\"Antonella\",\"last_name\":\"Sharrard\",\"email\":\"asharrardia@ebay.co.uk\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":57,\"birthday\":\"2007-07-28\"}\n{\"id\":660,\"first_name\":\"Rosy\",\"last_name\":\"Paulon\",\"email\":\"rpaulonib@tamu.edu\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":41,\"birthday\":\"1993-10-22\"}\n{\"id\":661,\"first_name\":\"Daphene\",\"last_name\":\"Leinthall\",\"email\":\"dleinthallic@domainmarket.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":65,\"birthday\":\"2008-03-23\"}\n{\"id\":662,\"first_name\":\"Peterus\",\"last_name\":\"Langsbury\",\"email\":\"plangsburyid@accuweather.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":23,\"birthday\":\"2004-07-13\"}\n{\"id\":663,\"first_name\":\"Melinde\",\"last_name\":\"Cromwell\",\"email\":\"mcromwellie@devhub.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":70,\"birthday\":\"1999-07-20\"}\n{\"id\":664,\"first_name\":\"Florencia\",\"last_name\":\"Elverston\",\"email\":\"felverstonif@opensource.org\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":45,\"birthday\":\"1993-08-26\"}\n{\"id\":665,\"first_name\":\"Barri\",\"last_name\":\"Fegan\",\"email\":\"bfeganig@list-manage.com\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":32,\"birthday\":\"2006-03-14\"}\n{\"id\":666,\"first_name\":\"Eadmund\",\"last_name\":\"Croley\",\"email\":\"ecroleyih@webmd.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":25,\"birthday\":\"1993-05-03\"}\n{\"id\":667,\"first_name\":\"Lauralee\",\"last_name\":\"Cesaric\",\"email\":\"lcesaricii@mayoclinic.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":77,\"birthday\":\"1996-09-30\"}\n{\"id\":668,\"first_name\":\"Manny\",\"last_name\":\"Cornish\",\"email\":\"mcornishij@alibaba.com\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":9,\"birthday\":\"1996-02-20\"}\n{\"id\":669,\"first_name\":\"Florri\",\"last_name\":\"Zecchini\",\"email\":\"fzecchiniik@ca.gov\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":50,\"birthday\":\"1994-02-02\"}\n{\"id\":670,\"first_name\":\"Maddi\",\"last_name\":\"Goseling\",\"email\":\"mgoselingil@feedburner.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":13,\"birthday\":\"2001-12-31\"}\n{\"id\":671,\"first_name\":\"Broderick\",\"last_name\":\"Altofts\",\"email\":\"baltoftsim@goo.ne.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":66,\"birthday\":\"2009-01-02\"}\n{\"id\":672,\"first_name\":\"Cob\",\"last_name\":\"Condon\",\"email\":\"ccondonin@shutterfly.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":39,\"birthday\":\"2006-12-16\"}\n{\"id\":673,\"first_name\":\"Vanya\",\"last_name\":\"Cluatt\",\"email\":\"vcluattio@omniture.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":42,\"birthday\":\"2006-11-09\"}\n{\"id\":674,\"first_name\":\"Ilise\",\"last_name\":\"Machent\",\"email\":\"imachentip@prnewswire.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":34,\"birthday\":\"1997-01-28\"}\n{\"id\":675,\"first_name\":\"Nonah\",\"last_name\":\"Holbie\",\"email\":\"nholbieiq@youtu.be\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":57,\"birthday\":\"2002-01-02\"}\n{\"id\":676,\"first_name\":\"Phebe\",\"last_name\":\"Rentelll\",\"email\":\"prentelllir@fema.gov\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":1,\"birthday\":\"1994-07-15\"}\n{\"id\":677,\"first_name\":\"Lazar\",\"last_name\":\"Willars\",\"email\":\"lwillarsis@hugedomains.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":67,\"birthday\":\"1994-03-11\"}\n{\"id\":678,\"first_name\":\"Minni\",\"last_name\":\"Pauler\",\"email\":\"mpaulerit@behance.net\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":40,\"birthday\":\"2007-10-02\"}\n{\"id\":679,\"first_name\":\"Lizette\",\"last_name\":\"Fenne\",\"email\":\"lfenneiu@fema.gov\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":36,\"birthday\":\"1991-01-22\"}\n{\"id\":680,\"first_name\":\"Doloritas\",\"last_name\":\"Kiddey\",\"email\":\"dkiddeyiv@bbb.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":49,\"birthday\":\"1997-08-01\"}\n{\"id\":681,\"first_name\":\"Joice\",\"last_name\":\"Penvarden\",\"email\":\"jpenvardeniw@cam.ac.uk\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":64,\"birthday\":\"2008-02-21\"}\n{\"id\":682,\"first_name\":\"Catharina\",\"last_name\":\"Finders\",\"email\":\"cfindersix@bigcartel.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":9,\"birthday\":\"1999-03-09\"}\n{\"id\":683,\"first_name\":\"Art\",\"last_name\":\"Banville\",\"email\":\"abanvilleiy@oaic.gov.au\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":16,\"birthday\":\"2000-10-24\"}\n{\"id\":684,\"first_name\":\"Kent\",\"last_name\":\"Beaves\",\"email\":\"kbeavesiz@people.com.cn\",\"owns_house\":true,\"favorite_color\":null,\"age\":62,\"birthday\":\"2004-07-19\"}\n{\"id\":685,\"first_name\":\"Lilllie\",\"last_name\":\"Borland\",\"email\":\"lborlandj0@amazon.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":75,\"birthday\":\"1992-04-27\"}\n{\"id\":686,\"first_name\":\"Marlowe\",\"last_name\":\"Pavlov\",\"email\":\"mpavlovj1@usnews.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":44,\"birthday\":\"1990-06-27\"}\n{\"id\":687,\"first_name\":\"Herbie\",\"last_name\":\"O'Haire\",\"email\":\"hohairej2@go.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":56,\"birthday\":\"2001-01-17\"}\n{\"id\":688,\"first_name\":\"Darell\",\"last_name\":\"Kemmet\",\"email\":\"dkemmetj3@weebly.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":66,\"birthday\":\"2002-02-09\"}\n{\"id\":689,\"first_name\":\"Yvonne\",\"last_name\":\"Doerling\",\"email\":\"ydoerlingj4@paypal.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":13,\"birthday\":\"1993-03-01\"}\n{\"id\":690,\"first_name\":\"Johnnie\",\"last_name\":\"Salvidge\",\"email\":\"jsalvidgej5@freewebs.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":79,\"birthday\":\"2003-11-28\"}\n{\"id\":691,\"first_name\":\"Gregg\",\"last_name\":\"Lapsley\",\"email\":\"glapsleyj6@free.fr\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":22,\"birthday\":\"1997-09-28\"}\n{\"id\":692,\"first_name\":\"Kirby\",\"last_name\":\"Terrans\",\"email\":\"kterransj7@forbes.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":63,\"birthday\":\"1997-06-23\"}\n{\"id\":693,\"first_name\":\"Randi\",\"last_name\":\"Dennitts\",\"email\":\"rdennittsj8@tinyurl.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":76,\"birthday\":\"2008-10-14\"}\n{\"id\":694,\"first_name\":\"Joellen\",\"last_name\":\"Morot\",\"email\":\"jmorotj9@ycombinator.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":62,\"birthday\":\"2005-08-03\"}\n{\"id\":695,\"first_name\":\"Dynah\",\"last_name\":\"Brickett\",\"email\":\"dbrickettja@cornell.edu\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":38,\"birthday\":\"1994-07-15\"}\n{\"id\":696,\"first_name\":\"Dominick\",\"last_name\":\"Cudihy\",\"email\":\"dcudihyjb@ca.gov\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":15,\"birthday\":\"2000-02-14\"}\n{\"id\":697,\"first_name\":\"Zaria\",\"last_name\":\"Paal\",\"email\":\"zpaaljc@multiply.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":30,\"birthday\":\"2002-04-17\"}\n{\"id\":698,\"first_name\":\"Darin\",\"last_name\":\"Kemble\",\"email\":\"dkemblejd@ibm.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":55,\"birthday\":\"1999-09-18\"}\n{\"id\":699,\"first_name\":\"Cullan\",\"last_name\":\"Knewstub\",\"email\":\"cknewstubje@facebook.com\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":75,\"birthday\":\"1995-03-11\"}\n{\"id\":700,\"first_name\":\"Augustine\",\"last_name\":\"Deluze\",\"email\":\"adeluzejf@weather.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":15,\"birthday\":\"2007-03-09\"}\n{\"id\":701,\"first_name\":\"Jessi\",\"last_name\":\"Rodda\",\"email\":\"jroddajg@google.co.uk\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":49,\"birthday\":\"2006-08-04\"}\n{\"id\":702,\"first_name\":\"Clarinda\",\"last_name\":\"Saville\",\"email\":\"csavillejh@technorati.com\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":49,\"birthday\":\"2009-09-12\"}\n{\"id\":703,\"first_name\":\"Zitella\",\"last_name\":\"Leopard\",\"email\":\"zleopardji@usa.gov\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":77,\"birthday\":\"2003-12-05\"}\n{\"id\":704,\"first_name\":\"Phillipp\",\"last_name\":\"Salandino\",\"email\":\"psalandinojj@tuttocitta.it\",\"owns_house\":true,\"favorite_color\":null,\"age\":56,\"birthday\":\"2004-01-22\"}\n{\"id\":705,\"first_name\":\"Andrus\",\"last_name\":\"Figger\",\"email\":\"afiggerjk@boston.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":15,\"birthday\":\"2001-08-02\"}\n{\"id\":706,\"first_name\":\"Kim\",\"last_name\":\"Aloigi\",\"email\":\"kaloigijl@fastcompany.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":66,\"birthday\":\"1994-10-03\"}\n{\"id\":707,\"first_name\":\"Cal\",\"last_name\":\"Halfacre\",\"email\":\"chalfacrejm@surveymonkey.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":20,\"birthday\":\"1996-04-27\"}\n{\"id\":708,\"first_name\":\"Thebault\",\"last_name\":\"Ibberson\",\"email\":\"tibbersonjn@youku.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":4,\"birthday\":\"1998-03-09\"}\n{\"id\":709,\"first_name\":\"Norris\",\"last_name\":\"Brittle\",\"email\":\"nbrittlejo@furl.net\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":3,\"birthday\":\"2007-01-31\"}\n{\"id\":710,\"first_name\":\"Wilie\",\"last_name\":\"Caltera\",\"email\":\"wcalterajp@google.de\",\"owns_house\":true,\"favorite_color\":null,\"age\":66,\"birthday\":\"2006-11-15\"}\n{\"id\":711,\"first_name\":\"Lexi\",\"last_name\":\"Ryhorovich\",\"email\":\"lryhorovichjq@yolasite.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":33,\"birthday\":\"1996-01-19\"}\n{\"id\":712,\"first_name\":\"Shalom\",\"last_name\":\"Stilly\",\"email\":\"sstillyjr@ibm.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":40,\"birthday\":\"2002-03-24\"}\n{\"id\":713,\"first_name\":\"Yves\",\"last_name\":\"Sherr\",\"email\":\"ysherrjs@prweb.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":64,\"birthday\":\"2009-10-17\"}\n{\"id\":714,\"first_name\":\"Alethea\",\"last_name\":\"Hutchins\",\"email\":\"ahutchinsjt@gravatar.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":66,\"birthday\":\"1991-12-02\"}\n{\"id\":715,\"first_name\":\"Kassi\",\"last_name\":\"Cometson\",\"email\":\"kcometsonju@tinyurl.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":75,\"birthday\":\"1999-03-29\"}\n{\"id\":716,\"first_name\":\"Cassaundra\",\"last_name\":\"Crannell\",\"email\":\"ccrannelljv@wired.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":10,\"birthday\":\"1999-01-19\"}\n{\"id\":717,\"first_name\":\"Erv\",\"last_name\":\"Mongan\",\"email\":\"emonganjw@jiathis.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":70,\"birthday\":\"1992-03-29\"}\n{\"id\":718,\"first_name\":\"Maureen\",\"last_name\":\"Pales\",\"email\":\"mpalesjx@newyorker.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":60,\"birthday\":\"1990-07-16\"}\n{\"id\":719,\"first_name\":\"Terri\",\"last_name\":\"Sleep\",\"email\":\"tsleepjy@freewebs.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":20,\"birthday\":\"2006-08-12\"}\n{\"id\":720,\"first_name\":\"Leoline\",\"last_name\":\"Egdal\",\"email\":\"legdaljz@artisteer.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":16,\"birthday\":\"2007-03-25\"}\n{\"id\":721,\"first_name\":\"Humfrid\",\"last_name\":\"Frankom\",\"email\":\"hfrankomk0@infoseek.co.jp\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":46,\"birthday\":\"1999-01-17\"}\n{\"id\":722,\"first_name\":\"Miranda\",\"last_name\":\"Gillbard\",\"email\":\"mgillbardk1@europa.eu\",\"owns_house\":false,\"favorite_color\":null,\"age\":35,\"birthday\":\"1992-11-17\"}\n{\"id\":723,\"first_name\":\"Klaus\",\"last_name\":\"Sacher\",\"email\":\"ksacherk2@google.it\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":32,\"birthday\":\"1991-07-21\"}\n{\"id\":724,\"first_name\":\"Birdie\",\"last_name\":\"Favill\",\"email\":\"bfavillk3@storify.com\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":39,\"birthday\":\"1995-04-30\"}\n{\"id\":725,\"first_name\":\"Sherwynd\",\"last_name\":\"Tern\",\"email\":\"sternk4@ebay.co.uk\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":51,\"birthday\":\"2000-06-14\"}\n{\"id\":726,\"first_name\":\"Christabella\",\"last_name\":\"Hawton\",\"email\":\"chawtonk5@technorati.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":22,\"birthday\":\"2005-11-14\"}\n{\"id\":727,\"first_name\":\"Dominica\",\"last_name\":\"Branni\",\"email\":\"dbrannik6@eepurl.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":45,\"birthday\":\"2001-10-04\"}\n{\"id\":728,\"first_name\":\"Perren\",\"last_name\":\"Kordas\",\"email\":\"pkordask7@barnesandnoble.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":19,\"birthday\":\"1998-05-25\"}\n{\"id\":729,\"first_name\":\"Johnath\",\"last_name\":\"Blaney\",\"email\":\"jblaneyk8@soundcloud.com\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":40,\"birthday\":\"1996-12-13\"}\n{\"id\":730,\"first_name\":\"Madelene\",\"last_name\":\"Ganforthe\",\"email\":\"mganforthek9@sphinn.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":20,\"birthday\":\"1996-05-09\"}\n{\"id\":731,\"first_name\":\"Stanleigh\",\"last_name\":\"Shavel\",\"email\":\"sshavelka@upenn.edu\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":70,\"birthday\":\"1992-06-06\"}\n{\"id\":732,\"first_name\":\"Ira\",\"last_name\":\"Puddephatt\",\"email\":\"ipuddephattkb@de.vu\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":43,\"birthday\":\"2000-04-14\"}\n{\"id\":733,\"first_name\":\"Byran\",\"last_name\":\"Drayson\",\"email\":\"bdraysonkc@mashable.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":62,\"birthday\":\"1998-02-27\"}\n{\"id\":734,\"first_name\":\"Arthur\",\"last_name\":\"Aleksandrev\",\"email\":\"aaleksandrevkd@github.io\",\"owns_house\":true,\"favorite_color\":null,\"age\":53,\"birthday\":\"1995-01-11\"}\n{\"id\":735,\"first_name\":\"Chrystel\",\"last_name\":\"Reisenstein\",\"email\":\"creisensteinke@theatlantic.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":46,\"birthday\":\"1994-08-04\"}\n{\"id\":736,\"first_name\":\"Maddalena\",\"last_name\":\"Angell\",\"email\":\"mangellkf@blogtalkradio.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":66,\"birthday\":\"2005-01-04\"}\n{\"id\":737,\"first_name\":\"Morgen\",\"last_name\":\"Vedeshkin\",\"email\":\"mvedeshkinkg@t-online.de\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":78,\"birthday\":\"1996-04-05\"}\n{\"id\":738,\"first_name\":\"Carita\",\"last_name\":\"Wyllis\",\"email\":\"cwylliskh@narod.ru\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":33,\"birthday\":\"1995-05-07\"}\n{\"id\":739,\"first_name\":\"Jillian\",\"last_name\":\"Reader\",\"email\":\"jreaderki@seattletimes.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":62,\"birthday\":\"1993-07-24\"}\n{\"id\":740,\"first_name\":\"Barby\",\"last_name\":\"Bosket\",\"email\":\"bbosketkj@constantcontact.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":19,\"birthday\":\"1993-09-07\"}\n{\"id\":741,\"first_name\":\"Hart\",\"last_name\":\"Lawrenz\",\"email\":\"hlawrenzkk@zdnet.com\",\"owns_house\":true,\"favorite_color\":\"Blue\",\"age\":67,\"birthday\":\"2002-12-03\"}\n{\"id\":742,\"first_name\":\"Cybil\",\"last_name\":\"Macci\",\"email\":\"cmaccikl@blogs.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":46,\"birthday\":\"2001-07-11\"}\n{\"id\":743,\"first_name\":\"Mil\",\"last_name\":\"Dressell\",\"email\":\"mdressellkm@t-online.de\",\"owns_house\":true,\"favorite_color\":null,\"age\":77,\"birthday\":\"1999-04-21\"}\n{\"id\":744,\"first_name\":\"Feliza\",\"last_name\":\"Lude\",\"email\":\"fludekn@meetup.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":5,\"birthday\":\"2008-02-02\"}\n{\"id\":745,\"first_name\":\"Ileane\",\"last_name\":\"Manolov\",\"email\":\"imanolovko@google.ru\",\"owns_house\":true,\"favorite_color\":null,\"age\":1,\"birthday\":\"1996-08-24\"}\n{\"id\":746,\"first_name\":\"Elsi\",\"last_name\":\"Fitzsimmons\",\"email\":\"efitzsimmonskp@amazon.co.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":17,\"birthday\":\"2005-01-02\"}\n{\"id\":747,\"first_name\":\"Charisse\",\"last_name\":\"Figger\",\"email\":\"cfiggerkq@admin.ch\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":51,\"birthday\":\"2005-01-12\"}\n{\"id\":748,\"first_name\":\"Rafa\",\"last_name\":\"Lagadu\",\"email\":\"rlagadukr@sakura.ne.jp\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":46,\"birthday\":\"2001-05-09\"}\n{\"id\":749,\"first_name\":\"Scotty\",\"last_name\":\"Colston\",\"email\":\"scolstonks@sohu.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":30,\"birthday\":\"1990-02-26\"}\n{\"id\":750,\"first_name\":\"Leonhard\",\"last_name\":\"Fellenor\",\"email\":\"lfellenorkt@cloudflare.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":7,\"birthday\":\"1991-05-14\"}\n{\"id\":751,\"first_name\":\"Masha\",\"last_name\":\"Belsher\",\"email\":\"mbelsherku@gizmodo.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":30,\"birthday\":\"1995-12-02\"}\n{\"id\":752,\"first_name\":\"Danella\",\"last_name\":\"McAster\",\"email\":\"dmcasterkv@nps.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":40,\"birthday\":\"1999-08-31\"}\n{\"id\":753,\"first_name\":\"Bran\",\"last_name\":\"Lannen\",\"email\":\"blannenkw@google.com.hk\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":34,\"birthday\":\"1998-10-01\"}\n{\"id\":754,\"first_name\":\"Charlotta\",\"last_name\":\"Rawlinson\",\"email\":\"crawlinsonkx@cbc.ca\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":10,\"birthday\":\"2007-03-21\"}\n{\"id\":755,\"first_name\":\"Peggy\",\"last_name\":\"Koenen\",\"email\":\"pkoenenky@joomla.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":37,\"birthday\":\"2009-12-02\"}\n{\"id\":756,\"first_name\":\"Cirstoforo\",\"last_name\":\"Oakly\",\"email\":\"coaklykz@storify.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":6,\"birthday\":\"1994-06-09\"}\n{\"id\":757,\"first_name\":\"Adore\",\"last_name\":\"Grinnikov\",\"email\":\"agrinnikovl0@pcworld.com\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":18,\"birthday\":\"1994-05-15\"}\n{\"id\":758,\"first_name\":\"Mavra\",\"last_name\":\"Jouannin\",\"email\":\"mjouanninl1@trellian.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":55,\"birthday\":\"2006-07-03\"}\n{\"id\":759,\"first_name\":\"Sunshine\",\"last_name\":\"Ambrosoli\",\"email\":\"sambrosolil2@quantcast.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":2,\"birthday\":\"2005-11-23\"}\n{\"id\":760,\"first_name\":\"Daisie\",\"last_name\":\"Greeve\",\"email\":\"dgreevel3@edublogs.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":50,\"birthday\":\"1999-12-06\"}\n{\"id\":761,\"first_name\":\"Natala\",\"last_name\":\"Stansby\",\"email\":\"nstansbyl4@aboutads.info\",\"owns_house\":false,\"favorite_color\":null,\"age\":15,\"birthday\":\"1998-01-07\"}\n{\"id\":762,\"first_name\":\"Editha\",\"last_name\":\"Jentges\",\"email\":\"ejentgesl5@cpanel.net\",\"owns_house\":false,\"favorite_color\":null,\"age\":1,\"birthday\":\"1994-01-25\"}\n{\"id\":763,\"first_name\":\"Morten\",\"last_name\":\"Lamberto\",\"email\":\"mlambertol6@sciencedirect.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":29,\"birthday\":\"1999-03-07\"}\n{\"id\":764,\"first_name\":\"Temple\",\"last_name\":\"Phare\",\"email\":\"tpharel7@trellian.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":21,\"birthday\":\"1992-11-28\"}\n{\"id\":765,\"first_name\":\"Zondra\",\"last_name\":\"Bondy\",\"email\":\"zbondyl8@timesonline.co.uk\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":80,\"birthday\":\"2002-07-30\"}\n{\"id\":766,\"first_name\":\"Eal\",\"last_name\":\"Blitz\",\"email\":\"eblitzl9@lycos.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":68,\"birthday\":\"2003-06-20\"}\n{\"id\":767,\"first_name\":\"Trista\",\"last_name\":\"Middlemist\",\"email\":\"tmiddlemistla@livejournal.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":61,\"birthday\":\"2005-09-24\"}\n{\"id\":768,\"first_name\":\"Johnny\",\"last_name\":\"MacCaghan\",\"email\":\"jmaccaghanlb@census.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":57,\"birthday\":\"2003-07-10\"}\n{\"id\":769,\"first_name\":\"Cordula\",\"last_name\":\"Collinge\",\"email\":\"ccollingelc@loc.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":76,\"birthday\":\"1991-07-19\"}\n{\"id\":770,\"first_name\":\"Nero\",\"last_name\":\"Brindley\",\"email\":\"nbrindleyld@macromedia.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":70,\"birthday\":\"2003-04-14\"}\n{\"id\":771,\"first_name\":\"Nettle\",\"last_name\":\"Ghiroldi\",\"email\":\"nghiroldile@tamu.edu\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":65,\"birthday\":\"2008-10-17\"}\n{\"id\":772,\"first_name\":\"Gannie\",\"last_name\":\"O'Hearn\",\"email\":\"gohearnlf@toplist.cz\",\"owns_house\":false,\"favorite_color\":null,\"age\":55,\"birthday\":\"2005-10-05\"}\n{\"id\":773,\"first_name\":\"Emanuele\",\"last_name\":\"Novak\",\"email\":\"enovaklg@nationalgeographic.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":11,\"birthday\":\"2001-09-15\"}\n{\"id\":774,\"first_name\":\"Mord\",\"last_name\":\"Gershom\",\"email\":\"mgershomlh@sitemeter.com\",\"owns_house\":true,\"favorite_color\":\"Maroon\",\"age\":36,\"birthday\":\"1993-03-05\"}\n{\"id\":775,\"first_name\":\"Nannie\",\"last_name\":\"Jeger\",\"email\":\"njegerli@va.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":34,\"birthday\":\"1999-07-12\"}\n{\"id\":776,\"first_name\":\"Averyl\",\"last_name\":\"Danko\",\"email\":\"adankolj@a8.net\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":71,\"birthday\":\"2009-01-15\"}\n{\"id\":777,\"first_name\":\"Moselle\",\"last_name\":\"Bricket\",\"email\":\"mbricketlk@ning.com\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":40,\"birthday\":\"2001-09-25\"}\n{\"id\":778,\"first_name\":\"Elayne\",\"last_name\":\"Redwin\",\"email\":\"eredwinll@mlb.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":28,\"birthday\":\"2007-02-25\"}\n{\"id\":779,\"first_name\":\"Merralee\",\"last_name\":\"Beinisch\",\"email\":\"mbeinischlm@theguardian.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":44,\"birthday\":\"1994-01-01\"}\n{\"id\":780,\"first_name\":\"Suzy\",\"last_name\":\"MacCarter\",\"email\":\"smaccarterln@fotki.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":73,\"birthday\":\"2002-01-13\"}\n{\"id\":781,\"first_name\":\"Aliza\",\"last_name\":\"Sandal\",\"email\":\"asandallo@bloomberg.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":11,\"birthday\":\"1993-10-27\"}\n{\"id\":782,\"first_name\":\"Babara\",\"last_name\":\"Faltin\",\"email\":\"bfaltinlp@bigcartel.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":9,\"birthday\":\"2007-05-13\"}\n{\"id\":783,\"first_name\":\"Locke\",\"last_name\":\"Verrier\",\"email\":\"lverrierlq@bloomberg.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":20,\"birthday\":\"1995-10-06\"}\n{\"id\":784,\"first_name\":\"Yardley\",\"last_name\":\"Heister\",\"email\":\"yheisterlr@wufoo.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":28,\"birthday\":\"2000-07-24\"}\n{\"id\":785,\"first_name\":\"Xenia\",\"last_name\":\"Maciak\",\"email\":\"xmaciakls@timesonline.co.uk\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":38,\"birthday\":\"1996-05-01\"}\n{\"id\":786,\"first_name\":\"Estevan\",\"last_name\":\"Stoffler\",\"email\":\"estofflerlt@ed.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":50,\"birthday\":\"2005-06-16\"}\n{\"id\":787,\"first_name\":\"Lanie\",\"last_name\":\"Brithman\",\"email\":\"lbrithmanlu@webeden.co.uk\",\"owns_house\":true,\"favorite_color\":null,\"age\":49,\"birthday\":\"2000-03-30\"}\n{\"id\":788,\"first_name\":\"Phillie\",\"last_name\":\"Farraway\",\"email\":\"pfarrawaylv@opera.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":18,\"birthday\":\"2002-05-13\"}\n{\"id\":789,\"first_name\":\"Oralee\",\"last_name\":\"Danev\",\"email\":\"odanevlw@jigsy.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":43,\"birthday\":\"1992-08-27\"}\n{\"id\":790,\"first_name\":\"Walther\",\"last_name\":\"Khan\",\"email\":\"wkhanlx@blogtalkradio.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":73,\"birthday\":\"2000-08-28\"}\n{\"id\":791,\"first_name\":\"Lemmy\",\"last_name\":\"Niemetz\",\"email\":\"lniemetzly@webmd.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":32,\"birthday\":\"2001-07-28\"}\n{\"id\":792,\"first_name\":\"Crissy\",\"last_name\":\"Comley\",\"email\":\"ccomleylz@skype.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":52,\"birthday\":\"1993-02-02\"}\n{\"id\":793,\"first_name\":\"Sammie\",\"last_name\":\"Salling\",\"email\":\"ssallingm0@stanford.edu\",\"owns_house\":true,\"favorite_color\":null,\"age\":44,\"birthday\":\"1990-12-13\"}\n{\"id\":794,\"first_name\":\"Mar\",\"last_name\":\"Spincks\",\"email\":\"mspincksm1@economist.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":14,\"birthday\":\"2002-11-17\"}\n{\"id\":795,\"first_name\":\"Aleta\",\"last_name\":\"Fazakerley\",\"email\":\"afazakerleym2@sciencedaily.com\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":39,\"birthday\":\"1995-04-20\"}\n{\"id\":796,\"first_name\":\"Conny\",\"last_name\":\"Ivashchenko\",\"email\":\"civashchenkom3@mail.ru\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":70,\"birthday\":\"2007-06-16\"}\n{\"id\":797,\"first_name\":\"Delphine\",\"last_name\":\"Grinishin\",\"email\":\"dgrinishinm4@unicef.org\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":60,\"birthday\":\"2006-02-23\"}\n{\"id\":798,\"first_name\":\"Julianne\",\"last_name\":\"Wythill\",\"email\":\"jwythillm5@weather.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":42,\"birthday\":\"2005-06-11\"}\n{\"id\":799,\"first_name\":\"Ianthe\",\"last_name\":\"Metherell\",\"email\":\"imetherellm6@gmpg.org\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":71,\"birthday\":\"1990-09-04\"}\n{\"id\":800,\"first_name\":\"Bab\",\"last_name\":\"Keyzor\",\"email\":\"bkeyzorm7@kickstarter.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":72,\"birthday\":\"1993-05-15\"}\n{\"id\":801,\"first_name\":\"Val\",\"last_name\":\"Whal\",\"email\":\"vwhalm8@dot.gov\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":80,\"birthday\":\"1996-03-16\"}\n{\"id\":802,\"first_name\":\"Essy\",\"last_name\":\"Husbands\",\"email\":\"ehusbandsm9@samsung.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":62,\"birthday\":\"2000-12-31\"}\n{\"id\":803,\"first_name\":\"Cello\",\"last_name\":\"Ioan\",\"email\":\"cioanma@jimdo.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":55,\"birthday\":\"2008-01-22\"}\n{\"id\":804,\"first_name\":\"Balduin\",\"last_name\":\"Marrows\",\"email\":\"bmarrowsmb@unicef.org\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":69,\"birthday\":\"1991-01-05\"}\n{\"id\":805,\"first_name\":\"Michel\",\"last_name\":\"Lurcock\",\"email\":\"mlurcockmc@bloomberg.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":50,\"birthday\":\"1993-12-30\"}\n{\"id\":806,\"first_name\":\"Augusta\",\"last_name\":\"Yuryshev\",\"email\":\"ayuryshevmd@weebly.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":4,\"birthday\":\"2008-07-20\"}\n{\"id\":807,\"first_name\":\"Lyon\",\"last_name\":\"Jozef\",\"email\":\"ljozefme@narod.ru\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":19,\"birthday\":\"1997-01-20\"}\n{\"id\":808,\"first_name\":\"Arnuad\",\"last_name\":\"Smails\",\"email\":\"asmailsmf@state.gov\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":6,\"birthday\":\"1993-11-16\"}\n{\"id\":809,\"first_name\":\"Maurise\",\"last_name\":\"Jory\",\"email\":\"mjorymg@msn.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":20,\"birthday\":\"2007-01-07\"}\n{\"id\":810,\"first_name\":\"Dilly\",\"last_name\":\"Brabin\",\"email\":\"dbrabinmh@rakuten.co.jp\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":72,\"birthday\":\"2001-02-07\"}\n{\"id\":811,\"first_name\":\"Cordey\",\"last_name\":\"Trusdale\",\"email\":\"ctrusdalemi@vinaora.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":23,\"birthday\":\"2001-09-09\"}\n{\"id\":812,\"first_name\":\"Janeva\",\"last_name\":\"Anniwell\",\"email\":\"janniwellmj@domainmarket.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":57,\"birthday\":\"2008-06-18\"}\n{\"id\":813,\"first_name\":\"Dianne\",\"last_name\":\"Colcutt\",\"email\":\"dcolcuttmk@xing.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":7,\"birthday\":\"1998-05-10\"}\n{\"id\":814,\"first_name\":\"Jermaine\",\"last_name\":\"Gillice\",\"email\":\"jgilliceml@illinois.edu\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":53,\"birthday\":\"2002-06-04\"}\n{\"id\":815,\"first_name\":\"Flory\",\"last_name\":\"Clothier\",\"email\":\"fclothiermm@meetup.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":74,\"birthday\":\"2009-10-28\"}\n{\"id\":816,\"first_name\":\"Ryann\",\"last_name\":\"O'Scanlan\",\"email\":\"roscanlanmn@china.com.cn\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":60,\"birthday\":\"1995-08-02\"}\n{\"id\":817,\"first_name\":\"Denys\",\"last_name\":\"Heynen\",\"email\":\"dheynenmo@seesaa.net\",\"owns_house\":true,\"favorite_color\":null,\"age\":41,\"birthday\":\"2007-06-23\"}\n{\"id\":818,\"first_name\":\"Yoko\",\"last_name\":\"Charpling\",\"email\":\"ycharplingmp@businessweek.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":43,\"birthday\":\"1991-01-23\"}\n{\"id\":819,\"first_name\":\"Cob\",\"last_name\":\"Scarlet\",\"email\":\"cscarletmq@cornell.edu\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":76,\"birthday\":\"2001-08-03\"}\n{\"id\":820,\"first_name\":\"Waly\",\"last_name\":\"Treasure\",\"email\":\"wtreasuremr@google.ru\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":64,\"birthday\":\"1995-01-08\"}\n{\"id\":821,\"first_name\":\"Ive\",\"last_name\":\"Exer\",\"email\":\"iexerms@twitter.com\",\"owns_house\":false,\"favorite_color\":\"Pink\",\"age\":75,\"birthday\":\"1997-03-10\"}\n{\"id\":822,\"first_name\":\"Hartwell\",\"last_name\":\"Hunnaball\",\"email\":\"hhunnaballmt@mit.edu\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":20,\"birthday\":\"2007-02-18\"}\n{\"id\":823,\"first_name\":\"Davidson\",\"last_name\":\"Dallison\",\"email\":\"ddallisonmu@typepad.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":70,\"birthday\":\"1998-06-21\"}\n{\"id\":824,\"first_name\":\"Andre\",\"last_name\":\"Park\",\"email\":\"aparkmv@nytimes.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":7,\"birthday\":\"1991-05-21\"}\n{\"id\":825,\"first_name\":\"Constantino\",\"last_name\":\"Pickhaver\",\"email\":\"cpickhavermw@dyndns.org\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":69,\"birthday\":\"1994-09-30\"}\n{\"id\":826,\"first_name\":\"Marlena\",\"last_name\":\"Kalinovich\",\"email\":\"mkalinovichmx@cnbc.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":3,\"birthday\":\"2005-01-31\"}\n{\"id\":827,\"first_name\":\"Eddi\",\"last_name\":\"Bompas\",\"email\":\"ebompasmy@last.fm\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":27,\"birthday\":\"1997-04-20\"}\n{\"id\":828,\"first_name\":\"Jenna\",\"last_name\":\"Keyzman\",\"email\":\"jkeyzmanmz@shutterfly.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":25,\"birthday\":\"1996-09-03\"}\n{\"id\":829,\"first_name\":\"Howie\",\"last_name\":\"Willcot\",\"email\":\"hwillcotn0@adobe.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":40,\"birthday\":\"2003-12-07\"}\n{\"id\":830,\"first_name\":\"Rosabel\",\"last_name\":\"Debling\",\"email\":\"rdeblingn1@google.ru\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":69,\"birthday\":\"2003-11-14\"}\n{\"id\":831,\"first_name\":\"Gilberto\",\"last_name\":\"Paolillo\",\"email\":\"gpaolillon2@123-reg.co.uk\",\"owns_house\":true,\"favorite_color\":null,\"age\":23,\"birthday\":\"2001-03-28\"}\n{\"id\":832,\"first_name\":\"Winfield\",\"last_name\":\"Polle\",\"email\":\"wpollen3@loc.gov\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":80,\"birthday\":\"1998-07-18\"}\n{\"id\":833,\"first_name\":\"Krystal\",\"last_name\":\"Melendez\",\"email\":\"kmelendezn4@pinterest.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":32,\"birthday\":\"1994-12-21\"}\n{\"id\":834,\"first_name\":\"Wendye\",\"last_name\":\"Stepto\",\"email\":\"wstepton5@msu.edu\",\"owns_house\":true,\"favorite_color\":null,\"age\":71,\"birthday\":\"2001-05-31\"}\n{\"id\":835,\"first_name\":\"Cathie\",\"last_name\":\"Busek\",\"email\":\"cbusekn6@sourceforge.net\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":50,\"birthday\":\"2005-09-12\"}\n{\"id\":836,\"first_name\":\"Boyce\",\"last_name\":\"Woodroffe\",\"email\":\"bwoodroffen7@army.mil\",\"owns_house\":true,\"favorite_color\":null,\"age\":58,\"birthday\":\"2009-03-02\"}\n{\"id\":837,\"first_name\":\"Camey\",\"last_name\":\"Maxworthy\",\"email\":\"cmaxworthyn8@last.fm\",\"owns_house\":true,\"favorite_color\":null,\"age\":57,\"birthday\":\"2009-03-26\"}\n{\"id\":838,\"first_name\":\"Linnea\",\"last_name\":\"Benardeau\",\"email\":\"lbenardeaun9@earthlink.net\",\"owns_house\":false,\"favorite_color\":\"Violet\",\"age\":60,\"birthday\":\"2006-12-12\"}\n{\"id\":839,\"first_name\":\"Skelly\",\"last_name\":\"Jenks\",\"email\":\"sjenksna@gmpg.org\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":32,\"birthday\":\"1992-03-13\"}\n{\"id\":840,\"first_name\":\"Arda\",\"last_name\":\"Crann\",\"email\":\"acrannnb@ihg.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":64,\"birthday\":\"1990-07-24\"}\n{\"id\":841,\"first_name\":\"Sheree\",\"last_name\":\"Bloxam\",\"email\":\"sbloxamnc@nydailynews.com\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":35,\"birthday\":\"2001-07-10\"}\n{\"id\":842,\"first_name\":\"Stacie\",\"last_name\":\"Rustidge\",\"email\":\"srustidgend@jimdo.com\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":47,\"birthday\":\"2008-01-05\"}\n{\"id\":843,\"first_name\":\"Dunc\",\"last_name\":\"Vinsen\",\"email\":\"dvinsenne@opensource.org\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":47,\"birthday\":\"2000-03-29\"}\n{\"id\":844,\"first_name\":\"Walton\",\"last_name\":\"Schubuser\",\"email\":\"wschubusernf@facebook.com\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":33,\"birthday\":\"2008-01-01\"}\n{\"id\":845,\"first_name\":\"Ewan\",\"last_name\":\"Fawlo\",\"email\":\"efawlong@t.co\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":11,\"birthday\":\"2000-03-08\"}\n{\"id\":846,\"first_name\":\"Mac\",\"last_name\":\"Haughan\",\"email\":\"mhaughannh@virginia.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":61,\"birthday\":\"1990-06-11\"}\n{\"id\":847,\"first_name\":\"Natalie\",\"last_name\":\"Leason\",\"email\":\"nleasonni@yahoo.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":4,\"birthday\":\"1990-11-04\"}\n{\"id\":848,\"first_name\":\"Torrie\",\"last_name\":\"Conniam\",\"email\":\"tconniamnj@house.gov\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":55,\"birthday\":\"2000-06-03\"}\n{\"id\":849,\"first_name\":\"Zora\",\"last_name\":\"Haggar\",\"email\":\"zhaggarnk@ibm.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":58,\"birthday\":\"2008-06-03\"}\n{\"id\":850,\"first_name\":\"Karly\",\"last_name\":\"Huddlestone\",\"email\":\"khuddlestonenl@webeden.co.uk\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":24,\"birthday\":\"2001-02-09\"}\n{\"id\":851,\"first_name\":\"Webster\",\"last_name\":\"Luxford\",\"email\":\"wluxfordnm@sogou.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":24,\"birthday\":\"1990-11-28\"}\n{\"id\":852,\"first_name\":\"Sheppard\",\"last_name\":\"Storror\",\"email\":\"sstorrornn@phoca.cz\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":44,\"birthday\":\"2008-10-29\"}\n{\"id\":853,\"first_name\":\"Karrah\",\"last_name\":\"Auckland\",\"email\":\"kaucklandno@spotify.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":54,\"birthday\":\"2002-04-21\"}\n{\"id\":854,\"first_name\":\"Conrad\",\"last_name\":\"Gallehock\",\"email\":\"cgallehocknp@uol.com.br\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":61,\"birthday\":\"2006-09-12\"}\n{\"id\":855,\"first_name\":\"Arnaldo\",\"last_name\":\"Tather\",\"email\":\"atathernq@wordpress.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":24,\"birthday\":\"2008-04-29\"}\n{\"id\":856,\"first_name\":\"Micky\",\"last_name\":\"Rosa\",\"email\":\"mrosanr@businessinsider.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":4,\"birthday\":\"2005-05-18\"}\n{\"id\":857,\"first_name\":\"Harbert\",\"last_name\":\"Collomosse\",\"email\":\"hcollomossens@icq.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":26,\"birthday\":\"2009-04-19\"}\n{\"id\":858,\"first_name\":\"Ferne\",\"last_name\":\"Daveley\",\"email\":\"fdaveleynt@globo.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":39,\"birthday\":\"2005-09-17\"}\n{\"id\":859,\"first_name\":\"Maria\",\"last_name\":\"Siderfin\",\"email\":\"msiderfinnu@domainmarket.com\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":14,\"birthday\":\"1999-10-25\"}\n{\"id\":860,\"first_name\":\"Nicolis\",\"last_name\":\"Pietz\",\"email\":\"npietznv@technorati.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":67,\"birthday\":\"1998-04-13\"}\n{\"id\":861,\"first_name\":\"Austin\",\"last_name\":\"Baynes\",\"email\":\"abaynesnw@bigcartel.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":80,\"birthday\":\"1993-09-16\"}\n{\"id\":862,\"first_name\":\"Llewellyn\",\"last_name\":\"Quakley\",\"email\":\"lquakleynx@forbes.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":54,\"birthday\":\"2009-01-04\"}\n{\"id\":863,\"first_name\":\"Norrie\",\"last_name\":\"Campany\",\"email\":\"ncampanyny@seesaa.net\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":46,\"birthday\":\"2004-09-05\"}\n{\"id\":864,\"first_name\":\"Adelle\",\"last_name\":\"Colbert\",\"email\":\"acolbertnz@seattletimes.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":1,\"birthday\":\"1995-10-18\"}\n{\"id\":865,\"first_name\":\"Ingamar\",\"last_name\":\"Behne\",\"email\":\"ibehneo0@youtu.be\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":9,\"birthday\":\"2003-09-13\"}\n{\"id\":866,\"first_name\":\"Daniella\",\"last_name\":\"Hurrion\",\"email\":\"dhurriono1@businessweek.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":56,\"birthday\":\"2004-04-10\"}\n{\"id\":867,\"first_name\":\"Harlan\",\"last_name\":\"Dentith\",\"email\":\"hdentitho2@columbia.edu\",\"owns_house\":true,\"favorite_color\":null,\"age\":60,\"birthday\":\"1995-04-27\"}\n{\"id\":868,\"first_name\":\"Sherie\",\"last_name\":\"Huband\",\"email\":\"shubando3@wikimedia.org\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":66,\"birthday\":\"2003-11-10\"}\n{\"id\":869,\"first_name\":\"Reggy\",\"last_name\":\"Garthshore\",\"email\":\"rgarthshoreo4@moonfruit.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":20,\"birthday\":\"2008-07-21\"}\n{\"id\":870,\"first_name\":\"Thibaud\",\"last_name\":\"Althrope\",\"email\":\"talthropeo5@geocities.jp\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":1,\"birthday\":\"2007-08-15\"}\n{\"id\":871,\"first_name\":\"Elonore\",\"last_name\":\"Kunkel\",\"email\":\"ekunkelo6@phoca.cz\",\"owns_house\":true,\"favorite_color\":null,\"age\":63,\"birthday\":\"1992-02-23\"}\n{\"id\":872,\"first_name\":\"Noelle\",\"last_name\":\"Gohn\",\"email\":\"ngohno7@flavors.me\",\"owns_house\":false,\"favorite_color\":\"Goldenrod\",\"age\":73,\"birthday\":\"1999-10-25\"}\n{\"id\":873,\"first_name\":\"Andreana\",\"last_name\":\"Curr\",\"email\":\"acurro8@storify.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":28,\"birthday\":\"2009-07-28\"}\n{\"id\":874,\"first_name\":\"Bethany\",\"last_name\":\"Pendry\",\"email\":\"bpendryo9@canalblog.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":58,\"birthday\":\"2009-11-11\"}\n{\"id\":875,\"first_name\":\"Cristionna\",\"last_name\":\"Ferreras\",\"email\":\"cferrerasoa@moonfruit.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":39,\"birthday\":\"2001-12-31\"}\n{\"id\":876,\"first_name\":\"Bartholemy\",\"last_name\":\"Langfat\",\"email\":\"blangfatob@craigslist.org\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":24,\"birthday\":\"2007-02-11\"}\n{\"id\":877,\"first_name\":\"Jeannie\",\"last_name\":\"Jordanson\",\"email\":\"jjordansonoc@prlog.org\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":53,\"birthday\":\"2005-04-30\"}\n{\"id\":878,\"first_name\":\"Amble\",\"last_name\":\"Grotty\",\"email\":\"agrottyod@huffingtonpost.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":68,\"birthday\":\"2006-05-21\"}\n{\"id\":879,\"first_name\":\"Zeb\",\"last_name\":\"McMillam\",\"email\":\"zmcmillamoe@indiegogo.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":32,\"birthday\":\"1992-08-08\"}\n{\"id\":880,\"first_name\":\"Rosa\",\"last_name\":\"De Vuyst\",\"email\":\"rdevuystof@thetimes.co.uk\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":17,\"birthday\":\"2004-09-23\"}\n{\"id\":881,\"first_name\":\"Kimbra\",\"last_name\":\"Worthington\",\"email\":\"kworthingtonog@zdnet.com\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":62,\"birthday\":\"2000-07-27\"}\n{\"id\":882,\"first_name\":\"Dionysus\",\"last_name\":\"Derham\",\"email\":\"dderhamoh@imdb.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":31,\"birthday\":\"2008-07-21\"}\n{\"id\":883,\"first_name\":\"Ugo\",\"last_name\":\"Shinner\",\"email\":\"ushinneroi@hc360.com\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":14,\"birthday\":\"2006-10-20\"}\n{\"id\":884,\"first_name\":\"Lisle\",\"last_name\":\"Frail\",\"email\":\"lfrailoj@ovh.net\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":37,\"birthday\":\"1994-02-15\"}\n{\"id\":885,\"first_name\":\"Bliss\",\"last_name\":\"Willimont\",\"email\":\"bwillimontok@themeforest.net\",\"owns_house\":false,\"favorite_color\":\"Orange\",\"age\":77,\"birthday\":\"1993-08-18\"}\n{\"id\":886,\"first_name\":\"Rabi\",\"last_name\":\"Gilfoyle\",\"email\":\"rgilfoyleol@w3.org\",\"owns_house\":true,\"favorite_color\":null,\"age\":6,\"birthday\":\"2006-01-24\"}\n{\"id\":887,\"first_name\":\"Quinton\",\"last_name\":\"Rosgen\",\"email\":\"qrosgenom@sfgate.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":62,\"birthday\":\"2006-04-17\"}\n{\"id\":888,\"first_name\":\"Glynda\",\"last_name\":\"McGilroy\",\"email\":\"gmcgilroyon@un.org\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":47,\"birthday\":\"2009-05-23\"}\n{\"id\":889,\"first_name\":\"Idelle\",\"last_name\":\"Beange\",\"email\":\"ibeangeoo@reuters.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":41,\"birthday\":\"1993-12-01\"}\n{\"id\":890,\"first_name\":\"Dian\",\"last_name\":\"Port\",\"email\":\"dportop@about.com\",\"owns_house\":true,\"favorite_color\":\"Green\",\"age\":3,\"birthday\":\"2008-04-10\"}\n{\"id\":891,\"first_name\":\"Rebecka\",\"last_name\":\"Wynn\",\"email\":\"rwynnoq@columbia.edu\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":18,\"birthday\":\"2002-08-27\"}\n{\"id\":892,\"first_name\":\"Sky\",\"last_name\":\"Skerme\",\"email\":\"sskermeor@twitpic.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":41,\"birthday\":\"1998-07-25\"}\n{\"id\":893,\"first_name\":\"Micky\",\"last_name\":\"Lude\",\"email\":\"mludeos@unblog.fr\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":75,\"birthday\":\"1997-02-28\"}\n{\"id\":894,\"first_name\":\"Sharron\",\"last_name\":\"Mallebone\",\"email\":\"smalleboneot@bloomberg.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":22,\"birthday\":\"1999-03-02\"}\n{\"id\":895,\"first_name\":\"Jephthah\",\"last_name\":\"Rillatt\",\"email\":\"jrillattou@timesonline.co.uk\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":38,\"birthday\":\"2006-11-29\"}\n{\"id\":896,\"first_name\":\"Zara\",\"last_name\":\"Leachman\",\"email\":\"zleachmanov@ucla.edu\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":66,\"birthday\":\"2000-12-25\"}\n{\"id\":897,\"first_name\":\"Eal\",\"last_name\":\"Farguhar\",\"email\":\"efarguharow@photobucket.com\",\"owns_house\":false,\"favorite_color\":\"Indigo\",\"age\":68,\"birthday\":\"2007-03-22\"}\n{\"id\":898,\"first_name\":\"Quentin\",\"last_name\":\"Ivanets\",\"email\":\"qivanetsox@parallels.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":60,\"birthday\":\"1997-03-19\"}\n{\"id\":899,\"first_name\":\"Anders\",\"last_name\":\"Domerq\",\"email\":\"adomerqoy@wufoo.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":4,\"birthday\":\"2005-03-12\"}\n{\"id\":900,\"first_name\":\"Ty\",\"last_name\":\"Prudence\",\"email\":\"tprudenceoz@free.fr\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":10,\"birthday\":\"2000-08-22\"}\n{\"id\":901,\"first_name\":\"Gun\",\"last_name\":\"Ishaki\",\"email\":\"gishakip0@state.tx.us\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":10,\"birthday\":\"2007-02-20\"}\n{\"id\":902,\"first_name\":\"Ronald\",\"last_name\":\"Aizikovitch\",\"email\":\"raizikovitchp1@free.fr\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":16,\"birthday\":\"1999-01-12\"}\n{\"id\":903,\"first_name\":\"Jolene\",\"last_name\":\"Heavens\",\"email\":\"jheavensp2@bigcartel.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":15,\"birthday\":\"1990-11-26\"}\n{\"id\":904,\"first_name\":\"Reginald\",\"last_name\":\"Delafont\",\"email\":\"rdelafontp3@dot.gov\",\"owns_house\":true,\"favorite_color\":null,\"age\":6,\"birthday\":\"2000-04-15\"}\n{\"id\":905,\"first_name\":\"Leonhard\",\"last_name\":\"Gelder\",\"email\":\"lgelderp4@abc.net.au\",\"owns_house\":false,\"favorite_color\":null,\"age\":15,\"birthday\":\"1998-10-27\"}\n{\"id\":906,\"first_name\":\"Kip\",\"last_name\":\"Filimore\",\"email\":\"kfilimorep5@netlog.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":78,\"birthday\":\"2009-09-08\"}\n{\"id\":907,\"first_name\":\"Fairleigh\",\"last_name\":\"Easby\",\"email\":\"feasbyp6@sina.com.cn\",\"owns_house\":true,\"favorite_color\":\"Orange\",\"age\":39,\"birthday\":\"2002-07-12\"}\n{\"id\":908,\"first_name\":\"Gran\",\"last_name\":\"Edelmann\",\"email\":\"gedelmannp7@oakley.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":60,\"birthday\":\"1994-01-03\"}\n{\"id\":909,\"first_name\":\"Valle\",\"last_name\":\"Gration\",\"email\":\"vgrationp8@mail.ru\",\"owns_house\":true,\"favorite_color\":null,\"age\":34,\"birthday\":\"1993-07-24\"}\n{\"id\":910,\"first_name\":\"Valentino\",\"last_name\":\"Kilmaster\",\"email\":\"vkilmasterp9@elegantthemes.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":80,\"birthday\":\"1990-06-11\"}\n{\"id\":911,\"first_name\":\"Dylan\",\"last_name\":\"Ibel\",\"email\":\"dibelpa@cargocollective.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":44,\"birthday\":\"1998-05-20\"}\n{\"id\":912,\"first_name\":\"Krissie\",\"last_name\":\"Thoresbie\",\"email\":\"kthoresbiepb@reference.com\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":21,\"birthday\":\"1997-07-19\"}\n{\"id\":913,\"first_name\":\"Maryjane\",\"last_name\":\"Belly\",\"email\":\"mbellypc@netscape.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":7,\"birthday\":\"2009-03-28\"}\n{\"id\":914,\"first_name\":\"Heather\",\"last_name\":\"Munro\",\"email\":\"hmunropd@ed.gov\",\"owns_house\":true,\"favorite_color\":\"Indigo\",\"age\":24,\"birthday\":\"2008-09-02\"}\n{\"id\":915,\"first_name\":\"Briano\",\"last_name\":\"Trott\",\"email\":\"btrottpe@topsy.com\",\"owns_house\":false,\"favorite_color\":\"Puce\",\"age\":47,\"birthday\":\"1990-11-26\"}\n{\"id\":916,\"first_name\":\"Zak\",\"last_name\":\"Vanshin\",\"email\":\"zvanshinpf@histats.com\",\"owns_house\":true,\"favorite_color\":\"Fuscia\",\"age\":77,\"birthday\":\"2005-09-29\"}\n{\"id\":917,\"first_name\":\"Janos\",\"last_name\":\"Ratt\",\"email\":\"jrattpg@nsw.gov.au\",\"owns_house\":true,\"favorite_color\":\"Violet\",\"age\":53,\"birthday\":\"1993-11-30\"}\n{\"id\":918,\"first_name\":\"Marci\",\"last_name\":\"Jansen\",\"email\":\"mjansenph@admin.ch\",\"owns_house\":true,\"favorite_color\":null,\"age\":9,\"birthday\":\"1992-03-06\"}\n{\"id\":919,\"first_name\":\"Hagan\",\"last_name\":\"Grundwater\",\"email\":\"hgrundwaterpi@ebay.co.uk\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":17,\"birthday\":\"2005-11-15\"}\n{\"id\":920,\"first_name\":\"Frazer\",\"last_name\":\"Clemenceau\",\"email\":\"fclemenceaupj@gmpg.org\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":20,\"birthday\":\"1990-12-21\"}\n{\"id\":921,\"first_name\":\"Grantley\",\"last_name\":\"Inkin\",\"email\":\"ginkinpk@cmu.edu\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":18,\"birthday\":\"2007-03-19\"}\n{\"id\":922,\"first_name\":\"Ollie\",\"last_name\":\"Gobell\",\"email\":\"ogobellpl@shop-pro.jp\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":56,\"birthday\":\"2007-06-26\"}\n{\"id\":923,\"first_name\":\"Aldin\",\"last_name\":\"Sallans\",\"email\":\"asallanspm@huffingtonpost.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":4,\"birthday\":\"1990-12-12\"}\n{\"id\":924,\"first_name\":\"Marigold\",\"last_name\":\"Gamet\",\"email\":\"mgametpn@reverbnation.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":12,\"birthday\":\"2007-07-30\"}\n{\"id\":925,\"first_name\":\"Rodge\",\"last_name\":\"Shmyr\",\"email\":\"rshmyrpo@dagondesign.com\",\"owns_house\":false,\"favorite_color\":\"Red\",\"age\":51,\"birthday\":\"1995-05-20\"}\n{\"id\":926,\"first_name\":\"Christabel\",\"last_name\":\"Pinfold\",\"email\":\"cpinfoldpp@zdnet.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":10,\"birthday\":\"1996-11-11\"}\n{\"id\":927,\"first_name\":\"Teador\",\"last_name\":\"Reach\",\"email\":\"treachpq@example.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":10,\"birthday\":\"2005-01-13\"}\n{\"id\":928,\"first_name\":\"Robinia\",\"last_name\":\"Drysdale\",\"email\":\"rdrysdalepr@ucoz.ru\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":5,\"birthday\":\"1995-08-04\"}\n{\"id\":929,\"first_name\":\"Chrissie\",\"last_name\":\"Bingle\",\"email\":\"cbingleps@mysql.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":4,\"birthday\":\"1997-10-14\"}\n{\"id\":930,\"first_name\":\"Chaddy\",\"last_name\":\"Masdin\",\"email\":\"cmasdinpt@yahoo.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":12,\"birthday\":\"2006-05-30\"}\n{\"id\":931,\"first_name\":\"Cody\",\"last_name\":\"Geall\",\"email\":\"cgeallpu@opera.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":12,\"birthday\":\"1997-10-26\"}\n{\"id\":932,\"first_name\":\"Johan\",\"last_name\":\"Dikelin\",\"email\":\"jdikelinpv@theatlantic.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":53,\"birthday\":\"2003-05-29\"}\n{\"id\":933,\"first_name\":\"Dev\",\"last_name\":\"Ten Broek\",\"email\":\"dtenbroekpw@samsung.com\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":78,\"birthday\":\"1996-09-06\"}\n{\"id\":934,\"first_name\":\"Ingunna\",\"last_name\":\"Farncombe\",\"email\":\"ifarncombepx@nba.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":34,\"birthday\":\"1997-02-04\"}\n{\"id\":935,\"first_name\":\"Maximilianus\",\"last_name\":\"Weavers\",\"email\":\"mweaverspy@soup.io\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":5,\"birthday\":\"2001-06-14\"}\n{\"id\":936,\"first_name\":\"Clary\",\"last_name\":\"Beardsworth\",\"email\":\"cbeardsworthpz@merriam-webster.com\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":31,\"birthday\":\"2003-10-26\"}\n{\"id\":937,\"first_name\":\"Ruddie\",\"last_name\":\"Warmington\",\"email\":\"rwarmingtonq0@cmu.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":59,\"birthday\":\"2001-02-02\"}\n{\"id\":938,\"first_name\":\"Carmella\",\"last_name\":\"Izzard\",\"email\":\"cizzardq1@archive.org\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":56,\"birthday\":\"2003-09-27\"}\n{\"id\":939,\"first_name\":\"Oliy\",\"last_name\":\"Lorman\",\"email\":\"olormanq2@chicagotribune.com\",\"owns_house\":true,\"favorite_color\":\"Puce\",\"age\":16,\"birthday\":\"2005-04-08\"}\n{\"id\":940,\"first_name\":\"Rita\",\"last_name\":\"Romero\",\"email\":\"rromeroq3@dell.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":37,\"birthday\":\"2009-07-24\"}\n{\"id\":941,\"first_name\":\"Netty\",\"last_name\":\"Gallamore\",\"email\":\"ngallamoreq4@webs.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":5,\"birthday\":\"2007-05-30\"}\n{\"id\":942,\"first_name\":\"Rorie\",\"last_name\":\"Barts\",\"email\":\"rbartsq5@vinaora.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":20,\"birthday\":\"2007-03-23\"}\n{\"id\":943,\"first_name\":\"Jerry\",\"last_name\":\"Kellaway\",\"email\":\"jkellawayq6@arizona.edu\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":75,\"birthday\":\"1992-08-17\"}\n{\"id\":944,\"first_name\":\"Ashlen\",\"last_name\":\"Zuker\",\"email\":\"azukerq7@ovh.net\",\"owns_house\":false,\"favorite_color\":null,\"age\":70,\"birthday\":\"1992-06-04\"}\n{\"id\":945,\"first_name\":\"Pearl\",\"last_name\":\"Grand\",\"email\":\"pgrandq8@alexa.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":27,\"birthday\":\"2002-08-08\"}\n{\"id\":946,\"first_name\":\"Timmie\",\"last_name\":\"Liggins\",\"email\":\"tligginsq9@arizona.edu\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":35,\"birthday\":\"2002-05-27\"}\n{\"id\":947,\"first_name\":\"Jethro\",\"last_name\":\"Fyfe\",\"email\":\"jfyfeqa@godaddy.com\",\"owns_house\":true,\"favorite_color\":\"Khaki\",\"age\":36,\"birthday\":\"2004-11-02\"}\n{\"id\":948,\"first_name\":\"Nealy\",\"last_name\":\"Mussolini\",\"email\":\"nmussoliniqb@simplemachines.org\",\"owns_house\":false,\"favorite_color\":null,\"age\":25,\"birthday\":\"1996-08-21\"}\n{\"id\":949,\"first_name\":\"Haze\",\"last_name\":\"Ondrus\",\"email\":\"hondrusqc@scribd.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":33,\"birthday\":\"2007-11-24\"}\n{\"id\":950,\"first_name\":\"Callida\",\"last_name\":\"Scroyton\",\"email\":\"cscroytonqd@accuweather.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":33,\"birthday\":\"1996-05-23\"}\n{\"id\":951,\"first_name\":\"Katrine\",\"last_name\":\"Leport\",\"email\":\"kleportqe@washington.edu\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":27,\"birthday\":\"2007-05-29\"}\n{\"id\":952,\"first_name\":\"Calhoun\",\"last_name\":\"Kahane\",\"email\":\"ckahaneqf@infoseek.co.jp\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":22,\"birthday\":\"2000-02-17\"}\n{\"id\":953,\"first_name\":\"Nanni\",\"last_name\":\"Baynam\",\"email\":\"nbaynamqg@indiegogo.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":34,\"birthday\":\"2008-10-05\"}\n{\"id\":954,\"first_name\":\"Sean\",\"last_name\":\"Page\",\"email\":\"spageqh@jalbum.net\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":47,\"birthday\":\"1997-12-10\"}\n{\"id\":955,\"first_name\":\"Sidoney\",\"last_name\":\"Nosworthy\",\"email\":\"snosworthyqi@facebook.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":41,\"birthday\":\"2009-11-19\"}\n{\"id\":956,\"first_name\":\"Octavia\",\"last_name\":\"Snedker\",\"email\":\"osnedkerqj@nationalgeographic.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":24,\"birthday\":\"2000-02-16\"}\n{\"id\":957,\"first_name\":\"Abbey\",\"last_name\":\"Byrne\",\"email\":\"abyrneqk@nsw.gov.au\",\"owns_house\":false,\"favorite_color\":null,\"age\":14,\"birthday\":\"1998-06-17\"}\n{\"id\":958,\"first_name\":\"Scotty\",\"last_name\":\"Lifsey\",\"email\":\"slifseyql@yellowbook.com\",\"owns_house\":false,\"favorite_color\":\"Crimson\",\"age\":54,\"birthday\":\"2003-03-28\"}\n{\"id\":959,\"first_name\":\"Alejandra\",\"last_name\":\"Daile\",\"email\":\"adaileqm@ft.com\",\"owns_house\":true,\"favorite_color\":\"Goldenrod\",\"age\":65,\"birthday\":\"1999-02-07\"}\n{\"id\":960,\"first_name\":\"1997-07-03T23:23:55Z\",\"last_name\":\"Picton\",\"email\":\"lpictonqn@delicious.com\",\"owns_house\":true,\"favorite_color\":\"Pink\",\"age\":11,\"birthday\":\"1992-07-26\"}\n{\"id\":961,\"first_name\":\"Rhonda\",\"last_name\":\"Benit\",\"email\":\"rbenitqo@github.io\",\"owns_house\":false,\"favorite_color\":\"Maroon\",\"age\":3,\"birthday\":\"2003-05-04\"}\n{\"id\":962,\"first_name\":\"Ambur\",\"last_name\":\"Marriner\",\"email\":\"amarrinerqp@salon.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":58,\"birthday\":\"1997-04-12\"}\n{\"id\":963,\"first_name\":\"Giraldo\",\"last_name\":\"Philott\",\"email\":\"gphilottqq@aol.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":27,\"birthday\":\"2006-06-25\"}\n{\"id\":964,\"first_name\":\"Parnell\",\"last_name\":\"Meiner\",\"email\":\"pmeinerqr@infoseek.co.jp\",\"owns_house\":false,\"favorite_color\":null,\"age\":9,\"birthday\":\"1997-10-12\"}\n{\"id\":965,\"first_name\":\"Friederike\",\"last_name\":\"Cutsforth\",\"email\":\"fcutsforthqs@gov.uk\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":73,\"birthday\":\"2005-09-26\"}\n{\"id\":966,\"first_name\":\"Patrick\",\"last_name\":\"Emanueli\",\"email\":\"pemanueliqt@nyu.edu\",\"owns_house\":false,\"favorite_color\":null,\"age\":28,\"birthday\":\"2003-03-01\"}\n{\"id\":967,\"first_name\":\"Daisie\",\"last_name\":\"Reyne\",\"email\":\"dreynequ@shop-pro.jp\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":50,\"birthday\":\"1993-06-14\"}\n{\"id\":968,\"first_name\":\"Geraldine\",\"last_name\":\"Hitzke\",\"email\":\"ghitzkeqv@irs.gov\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":40,\"birthday\":\"2007-09-10\"}\n{\"id\":969,\"first_name\":\"Haleigh\",\"last_name\":\"Tuck\",\"email\":\"htuckqw@yelp.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":77,\"birthday\":\"2007-10-14\"}\n{\"id\":970,\"first_name\":\"Kalil\",\"last_name\":\"Bearward\",\"email\":\"kbearwardqx@shutterfly.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":57,\"birthday\":\"2004-09-22\"}\n{\"id\":971,\"first_name\":\"Rudolfo\",\"last_name\":\"Beautyman\",\"email\":\"rbeautymanqy@vimeo.com\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":72,\"birthday\":\"1996-03-24\"}\n{\"id\":972,\"first_name\":\"Vannie\",\"last_name\":\"Dunstone\",\"email\":\"vdunstoneqz@pen.io\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":2,\"birthday\":\"1992-06-20\"}\n{\"id\":973,\"first_name\":\"Nettie\",\"last_name\":\"Ruscoe\",\"email\":\"nruscoer0@reference.com\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":56,\"birthday\":\"2008-07-17\"}\n{\"id\":974,\"first_name\":\"Dermot\",\"last_name\":\"Hacquoil\",\"email\":\"dhacquoilr1@bloomberg.com\",\"owns_house\":true,\"favorite_color\":\"Teal\",\"age\":80,\"birthday\":\"2006-10-27\"}\n{\"id\":975,\"first_name\":\"Tremain\",\"last_name\":\"Rowbottom\",\"email\":\"trowbottomr2@salon.com\",\"owns_house\":false,\"favorite_color\":\"Khaki\",\"age\":39,\"birthday\":\"2007-05-14\"}\n{\"id\":976,\"first_name\":\"Stefania\",\"last_name\":\"Rimmer\",\"email\":\"srimmerr3@exblog.jp\",\"owns_house\":false,\"favorite_color\":\"Green\",\"age\":15,\"birthday\":\"2004-05-04\"}\n{\"id\":977,\"first_name\":\"Patrice\",\"last_name\":\"Naile\",\"email\":\"pnailer4@adobe.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":34,\"birthday\":\"2006-04-19\"}\n{\"id\":978,\"first_name\":\"Jobi\",\"last_name\":\"Ullyott\",\"email\":\"jullyottr5@fema.gov\",\"owns_house\":true,\"favorite_color\":\"Yellow\",\"age\":55,\"birthday\":\"2007-01-03\"}\n{\"id\":979,\"first_name\":\"Catherin\",\"last_name\":\"Comsty\",\"email\":\"ccomstyr6@biblegateway.com\",\"owns_house\":true,\"favorite_color\":\"Mauv\",\"age\":58,\"birthday\":\"1999-09-02\"}\n{\"id\":980,\"first_name\":\"Morey\",\"last_name\":\"Akess\",\"email\":\"makessr7@bandcamp.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":20,\"birthday\":\"2005-02-17\"}\n{\"id\":981,\"first_name\":\"Padraic\",\"last_name\":\"Croyser\",\"email\":\"pcroyserr8@mail.ru\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":60,\"birthday\":\"1992-10-14\"}\n{\"id\":982,\"first_name\":\"Gale\",\"last_name\":\"Bennell\",\"email\":\"gbennellr9@4shared.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":51,\"birthday\":\"2007-07-30\"}\n{\"id\":983,\"first_name\":\"Lauralee\",\"last_name\":\"Mayston\",\"email\":\"lmaystonra@gmpg.org\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":30,\"birthday\":\"1997-11-16\"}\n{\"id\":984,\"first_name\":\"Ronica\",\"last_name\":\"Grim\",\"email\":\"rgrimrb@tiny.cc\",\"owns_house\":true,\"favorite_color\":\"Aquamarine\",\"age\":71,\"birthday\":\"1998-11-26\"}\n{\"id\":985,\"first_name\":\"Helaina\",\"last_name\":\"Ciccoloi\",\"email\":\"hciccoloirc@dell.com\",\"owns_house\":false,\"favorite_color\":\"Mauv\",\"age\":63,\"birthday\":\"2006-04-03\"}\n{\"id\":986,\"first_name\":\"Micaela\",\"last_name\":\"Turneaux\",\"email\":\"mturneauxrd@a8.net\",\"owns_house\":false,\"favorite_color\":null,\"age\":21,\"birthday\":\"1995-06-21\"}\n{\"id\":987,\"first_name\":\"Lanny\",\"last_name\":\"Risbridger\",\"email\":\"lrisbridgerre@reference.com\",\"owns_house\":false,\"favorite_color\":\"Turquoise\",\"age\":10,\"birthday\":\"2008-04-05\"}\n{\"id\":988,\"first_name\":\"Elsbeth\",\"last_name\":\"Stellino\",\"email\":\"estellinorf@cdbaby.com\",\"owns_house\":true,\"favorite_color\":\"Crimson\",\"age\":30,\"birthday\":\"2002-02-05\"}\n{\"id\":989,\"first_name\":\"Pete\",\"last_name\":\"Jorioz\",\"email\":\"pjoriozrg@howstuffworks.com\",\"owns_house\":false,\"favorite_color\":\"Blue\",\"age\":16,\"birthday\":\"2007-05-17\"}\n{\"id\":990,\"first_name\":\"Dania\",\"last_name\":\"Tipling\",\"email\":\"dtiplingrh@cocolog-nifty.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":12,\"birthday\":\"2006-03-31\"}\n{\"id\":991,\"first_name\":\"Gwendolyn\",\"last_name\":\"Cunnah\",\"email\":\"gcunnahri@sohu.com\",\"owns_house\":true,\"favorite_color\":\"Turquoise\",\"age\":19,\"birthday\":\"1996-10-10\"}\n{\"id\":992,\"first_name\":\"Tommie\",\"last_name\":\"Normanvell\",\"email\":\"tnormanvellrj@patch.com\",\"owns_house\":false,\"favorite_color\":\"Fuscia\",\"age\":75,\"birthday\":\"2002-10-02\"}\n{\"id\":993,\"first_name\":\"Jenine\",\"last_name\":\"De Biaggi\",\"email\":\"jdebiaggirk@yellowpages.com\",\"owns_house\":false,\"favorite_color\":\"Teal\",\"age\":70,\"birthday\":\"2009-10-23\"}\n{\"id\":994,\"first_name\":\"Ede\",\"last_name\":\"Beddo\",\"email\":\"ebeddorl@reference.com\",\"owns_house\":false,\"favorite_color\":\"Purple\",\"age\":76,\"birthday\":\"1997-08-21\"}\n{\"id\":995,\"first_name\":\"Fleurette\",\"last_name\":\"Chad\",\"email\":\"fchadrm@macromedia.com\",\"owns_house\":false,\"favorite_color\":\"Yellow\",\"age\":11,\"birthday\":\"1994-07-01\"}\n{\"id\":996,\"first_name\":\"Sydney\",\"last_name\":\"Galliford\",\"email\":\"sgallifordrn@comsenz.com\",\"owns_house\":false,\"favorite_color\":null,\"age\":72,\"birthday\":\"2001-08-20\"}\n{\"id\":997,\"first_name\":\"Noelani\",\"last_name\":\"Shearwood\",\"email\":\"nshearwoodro@intel.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":65,\"birthday\":\"2009-06-13\"}\n{\"id\":998,\"first_name\":\"Ruttger\",\"last_name\":\"Guerriero\",\"email\":\"rguerrierorp@drupal.org\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":8,\"birthday\":\"2005-09-22\"}\n{\"id\":999,\"first_name\":\"Huntley\",\"last_name\":\"Lerohan\",\"email\":\"hlerohanrq@howstuffworks.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":28,\"birthday\":\"1994-11-23\"}\n{\"id\":1000,\"first_name\":\"Elsie\",\"last_name\":\"Dives\",\"email\":\"edivesrr@artisteer.com\",\"owns_house\":true,\"favorite_color\":null,\"age\":32,\"birthday\":\"1995-06-02\"}\n{\"id\":1001,\"first_name\":\"Alice\",\"last_name\":\"Ali%%\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n{\"id\":1002,\"first_name\":\"Alice\",\"last_name\":\"Ali%\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n{\"id\":1003,\"first_name\":\"Alic%\",\"last_name\":\"Alic/%\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n{\"id\":1004,\"first_name\":\"Alic%\",\"last_name\":\"Alic^%\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n{\"id\":1005,\"first_name\":\"Alic%\",\"last_name\":\"Alic%%\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n{\"id\":1006,\"first_name\":\"Alic%\",\"last_name\":\"Alic%%%%%\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n{\"id\":1007,\"first_name\":\"Moelani  \",\"last_name\":\"Shearwood\",\"email\":\"nshearwoodro@intel.com\",\"owns_house\":false,\"favorite_color\":\"Aquamarine\",\"age\":65,\"birthday\":\"2009-06-13\"}\n{\"id\":1008,\"first_name\":\"      Ruttger\",\"last_name\":\"Guerriero\",\"email\":\"rguerrierorp@drupal.org\",\"owns_house\":true,\"favorite_color\":\"Purple\",\"age\":8,\"birthday\":\"2005-09-22\"}\n{\"id\":1009,\"first_name\":\"                   Huntley   \",\"last_name\":\"Lerohan\",\"email\":\"hlerohanrq@howstuffworks.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":28,\"birthday\":\"1994-11-23\"}\n{\"id\":1010,\"first_name\":\"                   Huntley   \",\"last_name\":\"Lerohan\",\"email\":\"hlerohanrq@howstuffworks.com\",\"owns_house\":true,\"favorite_color\":\"Red\",\"age\":100000,\"birthday\":\"1994-11-23\"}\n{\"id\":1000000000000000000,\"first_name\":\"a\",\"last_name\":\"b\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n{\"id\":1000000000000000001,\"first_name\":\"a\",\"last_name\":\"a\",\"email\":\"c\",\"owns_house\":true,\"favorite_color\":null,\"age\":45,\"birthday\":\"1995-06-01\"}\n"
  },
  {
    "path": "src/test/resources/log4j.properties",
    "content": "# Set everything to be logged to the console\nlog4j.rootCategory=ERROR, console\nlog4j.appender.console=org.apache.log4j.ConsoleAppender\nlog4j.appender.console.target=System.err\nlog4j.appender.console.layout=org.apache.log4j.PatternLayout\nlog4j.appender.console.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n\n\n# Settings to quiet third party logs that are too verbose\nlog4j.logger.org.eclipse.jetty=WARN\nlog4j.logger.org.eclipse.jetty.util.component.AbstractLifeCycle=ERROR\nlog4j.logger.org.apache.spark.repl.SparkIMain$exprTyper=ERROR\nlog4j.logger.org.apache.spark.repl.SparkILoop$SparkILoopInterpreter=ERROR\n\n# Make our logs LOUD\nlog4j.logger.com.singlestore.spark=DEBUG\n"
  },
  {
    "path": "src/test/resources/log4j2.properties",
    "content": "# Create STDOUT appender that writes data to the console\nappenders = console\nappender.console.type = Console\nappender.console.name = STDOUT\nappender.console.layout.type = PatternLayout\nappender.console.layout.pattern = %d{yy/MM/dd HH:mm:ss} %p %c{1}: %m%n\n\n# Set everything to be logged to the console\nrootLogger.level = ERROR\nrootLogger.appenderRef.stdout.ref = STDOUT\n\n# Make our logs LOUD\nloggers = singlestore\nlogger.singlestore.name = com.singlestore.spark\nlogger.singlestore.level = TRACE\nlogger.singlestore.appenderRef.stdout.ref = STDOUT\nlogger.singlestore.additivity = false\n"
  },
  {
    "path": "src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "content": "mock-maker-inline"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/BatchInsertBenchmark.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Connection, Date, DriverManager}\nimport java.time.LocalDate\nimport java.util.Properties\n\nimport org.apache.spark.sql.types._\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.{SaveMode, SparkSession}\n\nimport scala.util.Random\n\n// BatchInsertBenchmark is written to test batch insert with CPU profiler\n// this feature is accessible in Ultimate version of IntelliJ IDEA\n// see https://www.jetbrains.com/help/idea/async-profiler.html#profile for more details\nobject BatchInsertBenchmark extends App {\n  final val masterHost: String = sys.props.getOrElse(\"singlestore.host\", \"localhost\")\n  final val masterPort: String = sys.props.getOrElse(\"singlestore.port\", \"5506\")\n\n  val spark: SparkSession = SparkSession\n    .builder()\n    .master(\"local\")\n    .config(\"spark.sql.shuffle.partitions\", \"1\")\n    .config(\"spark.driver.bindAddress\", \"localhost\")\n    .config(\"spark.datasource.singlestore.ddlEndpoint\", s\"${masterHost}:${masterPort}\")\n    .config(\"spark.datasource.singlestore.database\", \"testdb\")\n    .getOrCreate()\n\n  def jdbcConnection: Loan[Connection] = {\n    val connProperties = new Properties()\n    connProperties.put(\"user\", \"root\")\n\n    Loan(\n      DriverManager.getConnection(\n        s\"jdbc:singlestore://$masterHost:$masterPort\",\n        connProperties\n      ))\n  }\n\n  def executeQuery(sql: String): Unit = {\n    jdbcConnection.to(conn => Loan(conn.createStatement).to(_.execute(sql)))\n  }\n\n  executeQuery(\"set global default_partitions_per_leaf = 2\")\n  executeQuery(\"drop database if exists testdb\")\n  executeQuery(\"create database testdb\")\n\n  def genDate() =\n    Date.valueOf(LocalDate.ofEpochDay(LocalDate.of(2001, 4, 11).toEpochDay + Random.nextInt(10000)))\n  def genRow(): (Long, Int, Double, String, Date) =\n    (Random.nextLong(), Random.nextInt(), Random.nextDouble(), Random.nextString(20), genDate())\n  val df =\n    spark.createDF(\n      List.fill(1000000)(genRow()),\n      List((\"LongType\", LongType, true),\n           (\"IntType\", IntegerType, true),\n           (\"DoubleType\", DoubleType, true),\n           (\"StringType\", StringType, true),\n           (\"DateType\", DateType, true))\n    )\n\n  val start = System.nanoTime()\n  df.write\n    .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n    .option(\"tableKey.primary\", \"IntType\")\n    .option(\"onDuplicateKeySQL\", \"IntType = IntType\")\n    .mode(SaveMode.Append)\n    .save(\"testdb.batchinsert\")\n\n  val diff = System.nanoTime() - start\n  println(\"Elapsed time: \" + diff + \"ns\")\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/BatchInsertTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.types.{IntegerType, StringType}\nimport org.apache.spark.sql.{DataFrame, SaveMode}\nimport org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}\n\nclass BatchInsertTest extends IntegrationSuiteBase with BeforeAndAfterEach with BeforeAndAfterAll {\n  var df: DataFrame = _\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n\n    df = spark.createDF(\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40)\n      ),\n      List((\"id\", IntegerType, false), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(SaveMode.Overwrite)\n      .option(\"tableKey.primary\", \"id\")\n      .save(\"testdb.batchinsert\")\n  }\n\n  it(\"insert into a new table\") {\n    df = spark.createDF(\n      List((5, \"Eric\", 5)),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"tableKey.primary\", \"id\")\n      .option(\"onDuplicateKeySQL\", \"age = age + 1\")\n      .option(\"insertBatchSize\", 10)\n      .mode(SaveMode.Append)\n      .save(\"testdb.batchinsertnew\")\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.batchinsertnew\")\n    assertSmallDataFrameEquality(actualDF, df)\n  }\n\n  def insertAndCheckContent(batchSize: Int,\n                            dfToInsert: List[Any],\n                            expectedContent: List[Any]): Unit = {\n    df = spark.createDF(\n      dfToInsert,\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n    insertValues(\"testdb.batchinsert\", df, \"age = age + 1\", batchSize)\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.batchinsert\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark\n        .createDF(\n          expectedContent,\n          List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n        ),\n      orderedComparison = false\n    )\n  }\n\n  it(\"insert a new row\") {\n    insertAndCheckContent(\n      10,\n      List((5, \"Eric\", 5)),\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40),\n        (5, \"Eric\", 5)\n      )\n    )\n  }\n\n  it(\"insert several new rows with small batchSize\") {\n    insertAndCheckContent(\n      2,\n      List(\n        (5, \"Jack\", 20),\n        (6, \"Mark\", 30),\n        (7, \"Fred\", 15),\n        (8, \"Jany\", 40),\n        (9, \"Monica\", 5)\n      ),\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40),\n        (5, \"Jack\", 20),\n        (6, \"Mark\", 30),\n        (7, \"Fred\", 15),\n        (8, \"Jany\", 40),\n        (9, \"Monica\", 5)\n      )\n    )\n  }\n\n  it(\"insert exactly batchSize rows\") {\n    insertAndCheckContent(\n      2,\n      List(\n        (5, \"Jack\", 20),\n        (6, \"Mark\", 30)\n      ),\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40),\n        (5, \"Jack\", 20),\n        (6, \"Mark\", 30)\n      )\n    )\n  }\n\n  it(\"negative batchsize\") {\n    insertAndCheckContent(\n      -2,\n      List(\n        (5, \"Jack\", 20),\n        (6, \"Mark\", 30)\n      ),\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40),\n        (5, \"Jack\", 20),\n        (6, \"Mark\", 30)\n      )\n    )\n  }\n\n  it(\"empty insert\") {\n    insertAndCheckContent(\n      2,\n      List(),\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40)\n      )\n    )\n  }\n\n  it(\"insert one existing row\") {\n    insertAndCheckContent(\n      2,\n      List((1, \"Jack\", 20)),\n      List(\n        (1, \"Jack\", 21),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40)\n      )\n    )\n  }\n\n  it(\"insert several existing rows\") {\n    insertAndCheckContent(\n      2,\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40)\n      ),\n      List(\n        (1, \"Jack\", 21),\n        (2, \"Dan\", 31),\n        (3, \"Bob\", 16),\n        (4, \"Alice\", 41)\n      )\n    )\n  }\n\n  it(\"insert existing and non existing row\") {\n    insertAndCheckContent(\n      2,\n      List(\n        (1, \"Jack\", 20),\n        (5, \"Mark\", 30)\n      ),\n      List(\n        (1, \"Jack\", 21),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40),\n        (5, \"Mark\", 30)\n      )\n    )\n  }\n\n  it(\"insert NULL\") {\n    insertAndCheckContent(\n      2,\n      List(\n        (5, null, null)\n      ),\n      List(\n        (1, \"Jack\", 20),\n        (2, \"Dan\", 30),\n        (3, \"Bob\", 15),\n        (4, \"Alice\", 40),\n        (5, null, null)\n      )\n    )\n  }\n\n  it(\"non-existing column\") {\n    executeQueryWithLog(\"DROP TABLE IF EXISTS batchinsert\")\n    executeQueryWithLog(\"CREATE TABLE batchinsert(id INT, name TEXT)\")\n\n    df = spark.createDF(\n      List((5, \"EBCEFGRHFED\" * 100, 50)),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n\n    try {\n      insertValues(\"testdb.batchinsert\", df, \"age = age + 1\", 10)\n      fail()\n    } catch {\n      case e: Exception if e.getMessage.contains(\"Unknown column 'age' in 'field list'\") =>\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/BenchmarkSerializingTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.{DataFrame, SaveMode}\nimport org.apache.spark.sql.types.{IntegerType, LongType, StringType}\n\nclass BenchmarkSerializingTest extends IntegrationSuiteBase {\n\n  val dbName    = \"testdb\"\n  val tableName = \"avro_table\"\n\n  val writeIterations = 10\n\n  val smallDataCount  = 1\n  val mediumDataCount = 1000\n  val largeDataCount  = 100000\n\n  val smallSchema = List((\"id\", StringType, false))\n  val mediumSchema = List((\"id\", StringType, false),\n                          (\"name\", StringType, false),\n                          (\"surname\", StringType, false),\n                          (\"age\", IntegerType, false))\n  val largeSchema = List(\n    (\"id\", StringType, false),\n    (\"name\", StringType, false),\n    (\"surname\", StringType, false),\n    (\"someString\", StringType, false),\n    (\"anotherString\", StringType, false),\n    (\"age\", IntegerType, false),\n    (\"secondNumber\", IntegerType, false),\n    (\"thirdNumber\", LongType, false)\n  )\n\n  def generateSmallData(index: Int)  = s\"$index\"\n  def generateMediumData(index: Int) = (s\"$index\", s\"name$index\", s\"surname$index\", index)\n  def generateLargeData(index: Int) =\n    (s\"$index\",\n     s\"name$index\",\n     s\"surname$index\",\n     s\"someString$index\",\n     s\"anotherString$index\",\n     index,\n     index + 1,\n     index * 2L)\n\n  val generateDataMap: Map[List[scala.Product], Int => Any] = Map(\n    smallSchema  -> generateSmallData,\n    mediumSchema -> generateMediumData,\n    largeSchema  -> generateLargeData\n  )\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n    executeQueryWithLog(s\"drop table if exists $dbName.$tableName\")\n  }\n\n  def doWriteOperation(dataFrame: DataFrame, options: Map[String, String]): Long = {\n    val startTime = System.currentTimeMillis()\n    for (_ <- 1 to writeIterations) {\n      dataFrame.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .mode(SaveMode.Append)\n        .options(options)\n        .save(s\"$dbName.$tableName\")\n    }\n    val endTime = System.currentTimeMillis()\n    endTime - startTime\n  }\n\n  def doTestOperation(dataCount: Int,\n                      schema: List[scala.Product],\n                      options: Map[String, String]): Unit = {\n    val dataFrameValues = List.tabulate(dataCount)(generateDataMap(schema))\n    val dataFrame =\n      spark.createDF(dataFrameValues, schema)\n    val timeSpend = doWriteOperation(dataFrame, options)\n    print(s\"Time spend for $writeIterations iterations: $timeSpend\")\n  }\n\n  describe(\"Avro testing\") {\n\n    it(\"small data | small schema\") {\n      doTestOperation(smallDataCount,\n                      smallSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"medium data | small schema\") {\n      doTestOperation(mediumDataCount,\n                      smallSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"large data | small schema\") {\n      doTestOperation(largeDataCount,\n                      smallSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"small data | medium schema\") {\n      doTestOperation(smallDataCount,\n                      mediumSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"medium data | medium schema\") {\n      doTestOperation(mediumDataCount,\n                      mediumSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"large data | medium schema\") {\n      doTestOperation(largeDataCount,\n                      mediumSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"small data | large schema\") {\n      doTestOperation(smallDataCount,\n                      largeSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"medium data | large schema\") {\n      doTestOperation(mediumDataCount,\n                      largeSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n\n    it(\"large data | large schema\") {\n      doTestOperation(largeDataCount,\n                      largeSchema,\n                      Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"))\n    }\n  }\n\n  describe(\"CSV testing\") {\n    it(\"small data | small schema\") {\n      doTestOperation(smallDataCount, smallSchema, Map.empty)\n    }\n\n    it(\"medium data | small schema\") {\n      doTestOperation(mediumDataCount, smallSchema, Map.empty)\n    }\n\n    it(\"large data | small schema\") {\n      doTestOperation(largeDataCount, smallSchema, Map.empty)\n    }\n\n    it(\"small data | medium schema\") {\n      doTestOperation(smallDataCount, mediumSchema, Map.empty)\n    }\n\n    it(\"medium data | medium schema\") {\n      doTestOperation(mediumDataCount, mediumSchema, Map.empty)\n    }\n\n    it(\"large data | medium schema\") {\n      doTestOperation(largeDataCount, mediumSchema, Map.empty)\n    }\n\n    it(\"small data | large schema\") {\n      doTestOperation(smallDataCount, largeSchema, Map.empty)\n    }\n\n    it(\"medium data | large schema\") {\n      doTestOperation(mediumDataCount, largeSchema, Map.empty)\n    }\n\n    it(\"large data | large schema\") {\n      doTestOperation(largeDataCount, largeSchema, Map.empty)\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/BinaryTypeBenchmark.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Connection, DriverManager}\nimport java.util.Properties\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.BatchInsertBenchmark.{df, executeQuery}\nimport org.apache.spark.sql.types.{BinaryType, IntegerType}\nimport org.apache.spark.sql.{SaveMode, SparkSession}\n\nimport scala.util.Random\n\n// BinaryTypeBenchmark is written to writing of the BinaryType with CPU profiler\n// this feature is accessible in Ultimate version of IntelliJ IDEA\n// see https://www.jetbrains.com/help/idea/async-profiler.html#profile for more details\nobject BinaryTypeBenchmark extends App {\n  final val masterHost: String = sys.props.getOrElse(\"singlestore.host\", \"localhost\")\n  final val masterPort: String = sys.props.getOrElse(\"singlestore.port\", \"5506\")\n\n  val spark: SparkSession = SparkSession\n    .builder()\n    .master(\"local\")\n    .config(\"spark.sql.shuffle.partitions\", \"1\")\n    .config(\"spark.driver.bindAddress\", \"localhost\")\n    .config(\"spark.datasource.singlestore.ddlEndpoint\", s\"${masterHost}:${masterPort}\")\n    .config(\"spark.datasource.singlestore.database\", \"testdb\")\n    .getOrCreate()\n\n  def jdbcConnection: Loan[Connection] = {\n    val connProperties = new Properties()\n    connProperties.put(\"user\", \"root\")\n\n    Loan(\n      DriverManager.getConnection(\n        s\"jdbc:singlestore://$masterHost:$masterPort\",\n        connProperties\n      ))\n  }\n\n  def executeQuery(sql: String): Unit = {\n    jdbcConnection.to(conn => Loan(conn.createStatement).to(_.execute(sql)))\n  }\n\n  executeQuery(\"set global default_partitions_per_leaf = 2\")\n  executeQuery(\"drop database if exists testdb\")\n  executeQuery(\"create database testdb\")\n\n  def genRandomByte(): Byte = (Random.nextInt(256) - 128).toByte\n  def genRandomRow(): Array[Byte] =\n    Array.fill(1000)(genRandomByte())\n\n  val df = spark.createDF(\n    List.fill(100000)(genRandomRow()).zipWithIndex,\n    List((\"data\", BinaryType, true), (\"id\", IntegerType, true))\n  )\n\n  val start1 = System.nanoTime()\n  df.write\n    .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n    .mode(SaveMode.Overwrite)\n    .save(\"testdb.LoadData\")\n\n  println(\"Elapsed time: \" + (System.nanoTime() - start1) + \"ns [LoadData CSV]\")\n\n  val start2 = System.nanoTime()\n  df.write\n    .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n    .option(\"tableKey.primary\", \"id\")\n    .option(\"onDuplicateKeySQL\", \"data = data\")\n    .mode(SaveMode.Overwrite)\n    .save(\"testdb.BatchInsert\")\n\n  println(\"Elapsed time: \" + (System.nanoTime() - start2) + \"ns [BatchInsert]\")\n\n  val avroStart = System.nanoTime()\n  df.write\n    .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n    .mode(SaveMode.Overwrite)\n    .option(SinglestoreOptions.LOAD_DATA_FORMAT, \"Avro\")\n    .save(\"testdb.AvroSerialization\")\n  println(\"Elapsed time: \" + (System.nanoTime() - avroStart) + \"ns [LoadData Avro] \")\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/CustomDatatypesTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Date, Timestamp}\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.SQLGen.SinglestoreVersion\nimport org.apache.spark.sql.types._\nimport org.apache.spark.sql.{DataFrame, SaveMode}\n\nimport scala.util.{Random, Try}\n\nclass CustomDatatypesTest extends IntegrationSuiteBase {\n\n  val dbName = \"testdb\"\n\n  def writeRead(dfToWrite: DataFrame,\n                expectedDf: DataFrame,\n                options: Map[String, String],\n                tableName: String): Unit = {\n    dfToWrite.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .options(options)\n      .mode(SaveMode.Overwrite)\n      .save(s\"testdb.$tableName\")\n\n    val actualDf =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(s\"testdb.$tableName\")\n\n    assertApproximateDataFrameEquality(\n      actualDf,\n      expectedDf,\n      precision = 0.01,\n      orderedComparison = false\n    )\n  }\n\n  describe(\"BooleanType\") {\n    // BooleanType is saved to SingleStore as TINYINT\n    // TINYINT is loaded from SingleStore as ShortType\n    def testBooleanType(options: Map[String, String], tableName: String): Unit = {\n      writeRead(\n        spark.createDF(\n          List(true, true, false, null).zipWithIndex,\n          List((\"data\", BooleanType, true), (\"id\", IntegerType, true))\n        ),\n        spark.createDF(\n          List(1: Byte, 1: Byte, 0: Byte, null).zipWithIndex,\n          List((\"data\", ByteType, true), (\"id\", IntegerType, true))\n        ),\n        options,\n        tableName\n      )\n    }\n\n    it(\"LoadDataWriter\") {\n      testBooleanType(\n        Map.empty,\n        \"BooleanTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testBooleanType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"BooleanTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"ByteType\") {\n    // ByteType is saved to SingleStore as TINYINT\n    // TINYINT is loaded from SingleStore as ShortType\n    def testByteType(options: Map[String, String], tableName: String): Unit = {\n      writeRead(\n        spark.createDF(\n          List(Byte.MinValue, 0: Byte, 6: Byte, Byte.MaxValue, null).zipWithIndex,\n          List((\"data\", ByteType, true), (\"id\", IntegerType, true))\n        ),\n        spark.createDF(\n          List(-128: Byte, 0: Byte, 6: Byte, 127: Byte, null).zipWithIndex,\n          List((\"data\", ByteType, true), (\"id\", IntegerType, true))\n        ),\n        options,\n        tableName\n      )\n    }\n\n    it(\"LoadDataWriter\") {\n      testByteType(\n        Map.empty,\n        \"ByteTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testByteType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"ByteTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"ShortType\") {\n    // ShortType is saved to SingleStore as SMALLINT\n    // SMALLINT is loaded from SingleStore as ShortType\n    def testShortType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(Short.MinValue, Short.MaxValue, 0: Short, 5: Short, -100: Short, null).zipWithIndex,\n        List((\"data\", ShortType, true), (\"id\", IntegerType, true))\n      )\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testShortType(\n        Map.empty,\n        \"ShortTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testShortType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"ShortTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"IntegerType\") {\n    // IntegerType is saved to SingleStore as INTEGER\n    // INTEGER is loaded from SingleStore as IntegerType\n    def testIntegerType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 5, -100, null).zipWithIndex,\n        List((\"data\", IntegerType, true), (\"id\", IntegerType, true))\n      )\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testIntegerType(\n        Map.empty,\n        \"IntegerTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testIntegerType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"IntegerTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"LongType\") {\n    // LongType is saved to SingleStore as BIGINT\n    // BIGINT is loaded from SingleStore as LongType\n    def testLongType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(Long.MinValue, Long.MaxValue, 0: Long, 5: Long, -100: Long, null).zipWithIndex,\n        List((\"data\", LongType, true), (\"id\", IntegerType, true))\n      )\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testLongType(\n        Map.empty,\n        \"LongTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testLongType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"LongTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"FloatType\") {\n    // FloatType is saved to SingleStore as FLOAT\n    // FLOAT is loaded from SingleStore as DoubleType\n    def testFloatType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(Float.MinPositiveValue, 0: Float, 5.45.toFloat, -100: Float, null).zipWithIndex,\n        List((\"data\", FloatType, true), (\"id\", IntegerType, true))\n      )\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testFloatType(\n        Map.empty,\n        \"FloatTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testFloatType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"FloatTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"DoubleType\") {\n    // DoubleType is saved to SingleStore as DOUBLE\n    // DOUBLE is loaded from SingleStore as DoubleType\n    def testDoubleType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(Double.MaxValue,\n             Double.MinValue,\n             Double.MinPositiveValue,\n             0.0: Double,\n             5.45: Double,\n             -100.0: Double,\n             null).zipWithIndex,\n        List((\"data\", DoubleType, true), (\"id\", IntegerType, true))\n      )\n\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testDoubleType(\n        Map.empty,\n        \"DoubleTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testDoubleType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"DoubleTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"StringType\") {\n    // StringType is saved to SingleStore as TEXT\n    // TEXT is loaded from SingleStore as StringType\n    def testStringType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(\"\",\n             \"AAAAAAAAAaaaaaa1234567890aaaAaaaaa\",\n             \"strstring\",\n             null,\n             \"\\\\\\t\\\\..<>\\n\\t\\\\,@!#$%^&*(\\\"'\").zipWithIndex,\n        List((\"data\", StringType, true), (\"id\", IntegerType, true))\n      )\n\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testStringType(\n        Map.empty,\n        \"StringTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testStringType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"StringTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"BinaryType\") {\n    // BinaryType is saved to SingleStore as BLOB\n    // BLOB is loaded from SingleStore as BinaryType\n    def testBinaryType(rows: List[Array[Byte]]): Unit = {\n      val df = spark.createDF(\n        rows.zipWithIndex,\n        List((\"data\", BinaryType, true), (\"id\", IntegerType, true))\n      )\n      testBinaryTypeDf(df)\n    }\n\n    def testBinaryTypeDf(df: DataFrame) = {\n      writeRead(df, df, Map.empty, \"BinaryTypeLoad\")\n      writeRead(df, df, Map(SinglestoreOptions.LOAD_DATA_FORMAT -> \"avro\"), \"BinaryTypeLoad\")\n      writeRead(df,\n                df,\n                Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n                \"BinaryTypeInsert\")\n    }\n\n    it(\"no rows\") {\n      testBinaryType(List())\n    }\n\n    it(\"one null\") {\n      testBinaryType(List(null))\n    }\n\n    it(\"a lot of nulls\") {\n      testBinaryType(List(null, null, null, null, null))\n    }\n\n    it(\"empty row\") {\n      testBinaryType(List(Array()))\n    }\n\n    it(\"a lot of empty rows\") {\n      testBinaryType(List(Array(), Array(), Array(), Array()))\n    }\n\n    it(\"one zero byte\") {\n      testBinaryType(List(Array[Byte](0)))\n    }\n\n    it(\"negative bytes\") {\n      testBinaryType(List(Array[Byte](-50, -128, -1, -20)))\n    }\n\n    it(\"special characters\") {\n      testBinaryType(List(Array[Byte]('\\t', '\\n', '\\\\')))\n    }\n\n    it(\"row with all possible bytes\") {\n      testBinaryType(List(Array.range(-128, 127).map(_.toByte)))\n    }\n\n    it(\"a lot of special characters\") {\n      val specialBytes = Array[Byte](0, 127, -128, '\\'', '\"', '`', '\\\\', '/', '\\t', '\\n', 't', 'n',\n        '\\f', 'f', '[', ']', '(', ')', '@', '#', ',', '.')\n      val sbLen = specialBytes.length\n\n      def genRandomSpecialByte(): Byte = specialBytes(Random.nextInt(sbLen))\n\n      def genRandomRow(): Array[Byte] =\n        Array.fill(100)(genRandomSpecialByte())\n\n      testBinaryType(List.fill(100)(genRandomRow()))\n    }\n\n    def genRandomByte(): Byte = (Random.nextInt(256) - 128).toByte\n\n    def genRandomRow(): Array[Byte] =\n      Array.fill(1000)(genRandomByte())\n\n    it(\"big random table\") {\n      testBinaryType(List.fill(1000)(genRandomRow()))\n    }\n\n    it(\"two binary types\") {\n      val df = spark.createDF(\n        List((genRandomRow(), genRandomRow(), 1), (genRandomRow(), genRandomRow(), 2)),\n        List((\"data\", BinaryType, true), (\"data2\", BinaryType, true), (\"id\", IntegerType, true))\n      )\n      testBinaryTypeDf(df)\n    }\n\n    it(\"multiple binary types\") {\n      val df = spark.createDF(\n        List(\n          (genRandomRow(), genRandomRow(), genRandomRow(), genRandomRow(), genRandomRow(), 1),\n          (genRandomRow(), genRandomRow(), genRandomRow(), genRandomRow(), genRandomRow(), 2)\n        ),\n        List(\n          (\"data\", BinaryType, true),\n          (\"data2\", BinaryType, true),\n          (\"data3\", BinaryType, true),\n          (\"data4\", BinaryType, true),\n          (\"data5\", BinaryType, true),\n          (\"id\", IntegerType, true)\n        )\n      )\n      testBinaryTypeDf(df)\n    }\n  }\n\n  describe(\"DateType\") {\n    // DateType is saved to SingleStore as DATE\n    // DATE is loaded from SingleStore as DateType\n    def testDateType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(\n          Date.valueOf(\"0001-01-01\"),\n          Date.valueOf(\"9999-12-31\"),\n          Date.valueOf(\"2001-04-11\"),\n          Date.valueOf(\"2001-4-11\"),\n          Date.valueOf(\"2020-01-5\"),\n          null\n        ).zipWithIndex,\n        List((\"data\", DateType, true), (\"id\", IntegerType, true))\n      )\n\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testDateType(\n        Map.empty,\n        \"DateTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testDateType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"DateTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"TimestampType\") {\n    // TimestampType is saved to SingleStore as TIMESTAMP(6)\n    // TIMESTAMP(6) is loaded from SingleStore as TimestampType\n    def testTimestampType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(\n          //new Timestamp(999), // Doesn't support [1970-01-01 00:00:00.999]\n          //new Timestamp(-10000), // Doesn't support [1969-12-31 23:59:50.0]\n          new Timestamp(1000), // Min supported timestamp [1970-01-01 00:00:01.000]\n          new Timestamp(12345),\n          new Timestamp(21474836L),\n          new Timestamp(2147483649L),\n          new Timestamp(214748364900L),\n          new Timestamp(2147483647999L) // Max supported timestamp [2038-01-19 03:14:07.999]\n          //new Timestamp(2147483648000L) // Doesn't support [2038-01-19 03:14:08.000]\n          // null // in SingleStore it will be saved as current timestamp\n        ).zipWithIndex,\n        List((\"data\", TimestampType, true), (\"id\", IntegerType, true))\n      )\n\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testTimestampType(\n        Map.empty,\n        \"TimestampTypeLoad\"\n      )\n    }\n    it(\"BatchInsertWriter\") {\n      testTimestampType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"TimestampTypeInsert\"\n      )\n    }\n  }\n\n  describe(\"DecimalType\") {\n    // DecimalType is saved to SingleStore as DECIMAL\n    // DECIMAL is loaded from SingleStore as DecimalType\n    // in SingleStore DECIMAL max precision is 60 when in spark it is 38\n    // during the reading from table with bigger precision values will be truncated\n    // in SingleStore DECIMAL max scale is 30 when in spark it is 38\n    // during the writing of dataFrame with bigger scale values will be truncated\n    def testDecimalType(options: Map[String, String], tableName: String): Unit = {\n      val df = spark.createDF(\n        List(\n          Decimal(123),\n          Decimal(123.123),\n          Decimal(-123.123.toFloat),\n          null\n        ).zipWithIndex,\n        List((\"data\", DecimalType(38, 30), true), (\"id\", IntegerType, true))\n      )\n      writeRead(df, df, options, tableName)\n    }\n\n    it(\"LoadDataWriter\") {\n      testDecimalType(\n        Map.empty,\n        \"DecimalTypeLoad\"\n      )\n    }\n\n    it(\"BatchInsertWriter\") {\n      testDecimalType(\n        Map(\"tableKey.primary\" -> \"id\", \"onDuplicateKeySQL\" -> \"data = data\"),\n        \"DecimalTypeInsert\"\n      )\n    }\n\n    it(\"big scale\") {\n      val df = spark.createDF(\n        List(\n          Decimal(123),\n          Decimal(123.123),\n          Decimal(-123.123.toFloat),\n          null\n        ).zipWithIndex,\n        List((\"data\", DecimalType(38, 32), true), (\"id\", IntegerType, true))\n      )\n\n      val writeResult = Try {\n        df.write\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n          .mode(SaveMode.Overwrite)\n          .save(s\"testdb.DecimalTypeBigScale\")\n      }\n      assert(writeResult.isFailure)\n      assert(\n        writeResult.failed.get.getMessage\n          .equals(\"Too big scale specified(32). SingleStore DECIMAL maximum scale is 30\"))\n    }\n\n    it(\"big precision\") {\n      executeQueryWithLog(\"drop table if exists testdb.DecimalTypeBigPrecision\")\n      executeQueryWithLog(\"create table testdb.DecimalTypeBigPrecision(a DECIMAL(65, 30))\")\n\n      val readResult = Try {\n        spark.read\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n          .load(s\"testdb.DecimalTypeBigPrecision\")\n      }\n      assert(readResult.isFailure)\n      assert(\n        readResult.failed.get.getCause.getMessage\n          .equals(\"DECIMAL precision 65 exceeds max precision 38\"))\n    }\n  }\n\n  it(\"JSON columns are treated as strings by Spark\") {\n    executeQueryWithLog(s\"\"\"\n                    |create table if not exists ${dbName}.basic (\n                    | j JSON\n                    |)\"\"\".stripMargin)\n\n    spark\n      .createDF(\n        List(\"\"\"{\"foo\":\"bar\"}\"\"\"),\n        List((\"j\", StringType, true))\n      )\n      .write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(SaveMode.Append)\n      .save(\"basic\")\n    val df = spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"basic\")\n    assertSmallDataFrameEquality(df,\n                                 spark\n                                   .createDF(\n                                     List(\"\"\"{\"foo\":\"bar\"}\"\"\"),\n                                     List((\"j\", StringType, true))\n                                   ))\n  }\n\n  it(\"BIT columns are treated as BinaryType\") {\n    executeQueryWithLog(\"drop table if exists testdb.BIT\")\n    executeQueryWithLog(\"create table testdb.BIT(a BIT)\")\n    executeQueryWithLog(\"insert into testdb.BIT values('010101'), ('00'), ('1'), (null)\")\n\n    val df = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .load(s\"testdb.BIT\")\n    assertSmallDataFrameEquality(\n      df,\n      spark.createDF(\n        List(\n          null,\n          Array[Byte](0, 0, '0', '1', '0', '1', '0', '1'),\n          Array[Byte](0, 0, 0, 0, 0, 0, '0', '0'),\n          Array[Byte](0, 0, 0, 0, 0, 0, 0, '1')\n        ),\n        List((\"a\", BinaryType, true))\n      ),\n      orderedComparison = false\n    )\n  }\n\n  it(\"TIME columns are treated as TimestampType\") {\n    executeQueryWithLog(\"drop table if exists testdb.TIME\")\n    executeQueryWithLog(\"create table testdb.TIME(a TIME)\")\n    executeQueryWithLog(\"insert into testdb.TIME values('-838:59:59'), (null), ('838:59:59')\")\n\n    val df = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .load(s\"testdb.TIME\")\n\n    assertSmallDataFrameEquality(\n      df,\n      spark.createDF(\n        // before Spark 3.2.0 JDBC TIME type was read as Timestamp\n        // in SPARK-34357 special handling of TIME type was added\n        // and it ignores date part\n        if (SinglestoreVersion(spark.version).atLeast(\"3.2.0\") && !SinglestoreVersion(spark.version)\n              .atLeast(\"4.0.0\")) {\n          List(null,\n               Timestamp.valueOf(\"1970-01-01 22:59:59.0\"),\n               Timestamp.valueOf(\"1970-01-01 01:00:01\"))\n        } else {\n          List(null,\n               Timestamp.valueOf(\"1970-02-04 22:59:59.0\"),\n               Timestamp.valueOf(\"1969-11-27 01:00:01\"))\n        },\n        List((\"a\", TimestampType, true))\n      ),\n      orderedComparison = false\n    )\n  }\n\n  describe(\"Avro serialization\") {\n\n    def insertAndAssertEquality(tableName: String,\n                                dataFrame: DataFrame,\n                                expectedDataFrame: DataFrame): Unit = {\n      dataFrame.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .mode(SaveMode.Overwrite)\n        .option(SinglestoreOptions.LOAD_DATA_FORMAT, \"avro\")\n        .save(s\"testdb.$tableName\")\n\n      val actualDF =\n        spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(s\"testdb.$tableName\")\n      assertLargeDataFrameEquality(actualDF, expectedDataFrame, orderedComparison = false)\n    }\n\n    it(\"should write StringType nullable\") {\n      val df = spark.createDF(\n        List((1, \"Alice\"), (2, null)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true))\n      )\n      insertAndAssertEquality(\"stringAvro\", df, df)\n    }\n\n    it(\"should write StringType\") {\n      val df = spark.createDF(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((\"id\", IntegerType, false), (\"name\", StringType, false))\n      )\n      val expectedDf = spark.createDF(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true))\n      )\n      insertAndAssertEquality(\"stringAvro\", df, expectedDf)\n    }\n\n    it(\"should write ShortType nullable\") {\n      val df = spark.createDF(\n        List((1, 21.toShort), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", ShortType, true))\n      )\n      insertAndAssertEquality(\"shortAvro\", df, df)\n    }\n\n    it(\"should write ShortType\") {\n      val df = spark.createDF(\n        List((1, 21.toShort), (2, -12.toShort)),\n        List((\"id\", IntegerType, false), (\"name\", ShortType, false))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 21.toShort), (2, -12.toShort)),\n        List((\"id\", IntegerType, true), (\"name\", ShortType, true))\n      )\n      insertAndAssertEquality(\"stringAvro\", df, expectedDf)\n    }\n\n    it(\"should write LongType nullable\") {\n      val df = spark.createDF(\n        List((1, 21L), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", LongType, true))\n      )\n      insertAndAssertEquality(\"longAvro\", df, df)\n    }\n\n    it(\"should write LongType\") {\n      val df = spark.createDF(\n        List((1, 21L), (2, 1000L)),\n        List((\"id\", IntegerType, false), (\"age\", LongType, false))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 21L), (2, 1000L)),\n        List((\"id\", IntegerType, true), (\"age\", LongType, true))\n      )\n      insertAndAssertEquality(\"longAvro\", df, expectedDf)\n    }\n\n    it(\"should write ByteType nullable\") {\n      val df = spark.createDF(\n        List((1, 21.byteValue()), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", ByteType, true))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 21.toByte), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", ByteType, true))\n      )\n      insertAndAssertEquality(\"byteAvro\", df, expectedDf)\n    }\n\n    it(\"should write ByteType\") {\n      val df = spark.createDF(\n        List((1, 21.byteValue()), (2, -12.byteValue())),\n        List((\"id\", IntegerType, false), (\"age\", ByteType, false))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 21.toByte), (2, -12.toByte)),\n        List((\"id\", IntegerType, true), (\"age\", ByteType, true))\n      )\n      insertAndAssertEquality(\"byteAvro\", df, expectedDf)\n    }\n\n    it(\"should write BooleanType nullable\") {\n      val df = spark.createDF(\n        List((1, true), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", BooleanType, true))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 1.toByte), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", ByteType, true))\n      )\n      insertAndAssertEquality(\"booleanAvro\", df, expectedDf)\n    }\n\n    it(\"should write BooleanType\") {\n      val df = spark.createDF(\n        List((1, true), (2, false)),\n        List((\"id\", IntegerType, false), (\"age\", BooleanType, false))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 1.toByte), (2, 0.toByte)),\n        List((\"id\", IntegerType, true), (\"age\", ByteType, true))\n      )\n      insertAndAssertEquality(\"booleanAvro\", df, expectedDf)\n    }\n\n    it(\"should write FloatType nullable\") {\n      val df = spark.createDF(\n        List((1, 21.123f), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", FloatType, true))\n      )\n      insertAndAssertEquality(\"floatAvro\", df, df)\n    }\n\n    it(\"should write FloatType\") {\n      val df = spark.createDF(\n        List((1, 21.123f), (2, 555.555f)),\n        List((\"id\", IntegerType, false), (\"age\", FloatType, false))\n      )\n\n      val expectedDf = spark.createDF(\n        List((1, 21.123f), (2, 555.555f)),\n        List((\"id\", IntegerType, true), (\"age\", FloatType, true))\n      )\n      insertAndAssertEquality(\"floatAvro\", df, expectedDf)\n    }\n\n    it(\"should write DoubleType nullable\") {\n      val df = spark.createDF(\n        List((1, 21.123123d), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", DoubleType, true))\n      )\n      insertAndAssertEquality(\"doubleAvro\", df, df)\n    }\n\n    it(\"should write DoubleType\") {\n      val df = spark.createDF(\n        List((1, 21.123123d), (2, 9999.99999d)),\n        List((\"id\", IntegerType, false), (\"age\", DoubleType, false))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 21.123123d), (2, 9999.99999d)),\n        List((\"id\", IntegerType, true), (\"age\", DoubleType, true))\n      )\n      insertAndAssertEquality(\"doubleAvro\", df, expectedDf)\n    }\n\n    it(\"should write DecimalType nullable\") {\n      val df = spark.createDF(\n        List((1, 215142: BigDecimal), (2, null)),\n        List((\"id\", IntegerType, true), (\"age\", DecimalType(10, 0), true))\n      )\n      insertAndAssertEquality(\"decimalAvro\", df, df)\n    }\n\n    it(\"should write DecimalType\") {\n      val df = spark.createDF(\n        List((1, 21235326: BigDecimal), (2, 9999999: BigDecimal)),\n        List((\"id\", IntegerType, false), (\"age\", DecimalType(10, 0), false))\n      )\n      val expectedDf = spark.createDF(\n        List((1, 21235326: BigDecimal), (2, 9999999: BigDecimal)),\n        List((\"id\", IntegerType, true), (\"age\", DecimalType(10, 0), true))\n      )\n      insertAndAssertEquality(\"decimalAvro\", df, expectedDf)\n    }\n\n  }\n\n  // Not supported types:\n  // CalendarIntervalType\n  // NullType\n  // ArrayType\n  // MapType\n\n  // Notes\n  // BatchInsertWriter fails to write a StringType with null byte ('\\0')\n  // DECIMAL and DecimalType have different maximum scale and precision. The error will happen if you try to read/write a table/dataFrame with wrong precision/scale\n  // TIMESTAMP in SingleStore support values from 1000 to 2147483647999. SingleStore treat null in TIMESTAMP column as current time\n  // Avro serialization doesn't support writing of TimestampType and DateType.\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/ExternalHostTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.PreparedStatement\nimport java.util.Properties\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.JdbcHelpers.getDDLConnProperties\nimport com.singlestore.spark.SQLGen.VariableList\nimport org.apache.spark.sql.DataFrame\nimport org.apache.spark.sql.execution.datasources.jdbc.{JDBCOptions, JdbcUtils}\nimport org.apache.spark.sql.types.{IntegerType, StringType}\nimport org.mockito.ArgumentMatchers.any\nimport org.mockito.MockitoSugar\nimport org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}\n\nclass ExternalHostTest\n    extends IntegrationSuiteBase\n    with BeforeAndAfterEach\n    with BeforeAndAfterAll\n    with MockitoSugar {\n\n  val testDb            = \"testdb\"\n  val testCollection    = \"externalHost\"\n  val mvNodesCollection = \"mv_nodes\"\n\n  var df: DataFrame = _\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.enableParallelRead\", \"forced\")\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.parallelRead.Features\", \"ReadFromLeaves\")\n    df = spark.createDF(\n      List((2, \"B\")),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true))\n    )\n    writeTable(s\"$testDb.$testCollection\", df)\n  }\n\n  def setupMockJdbcHelper(): Unit = {\n    when(JdbcHelpers.loadSchema(any[SinglestoreOptions], any[String], any[SQLGen.VariableList]))\n      .thenCallRealMethod()\n    when(JdbcHelpers.getDDLConnProperties(any[SinglestoreOptions], any[Boolean]))\n      .thenCallRealMethod()\n    when(JdbcHelpers.getDMLConnProperties(any[SinglestoreOptions], any[Boolean]))\n      .thenCallRealMethod()\n    when(JdbcHelpers.getConnProperties(any[SinglestoreOptions], any[Boolean], any[String]))\n      .thenCallRealMethod()\n    when(JdbcHelpers.explainJSONQuery(any[SinglestoreOptions], any[String], any[VariableList]))\n      .thenCallRealMethod()\n    when(JdbcHelpers.partitionHostPorts(any[SinglestoreOptions], any[String]))\n      .thenCallRealMethod()\n    when(JdbcHelpers.fillStatement(any[PreparedStatement], any[VariableList]))\n      .thenCallRealMethod()\n  }\n\n  describe(\"success tests\") {\n    it(\"low SingleStore version\") {\n\n      withObjectMocked[JdbcHelpers.type] {\n\n        setupMockJdbcHelper()\n        when(JdbcHelpers.getSinglestoreVersion(any[SinglestoreOptions])).thenReturn(\"6.8.10\")\n\n        val actualDF =\n          spark.read\n            .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n            .option(\"useExternalHost\", \"true\")\n            .load(s\"$testDb.$testCollection\")\n\n        assertSmallDataFrameEquality(\n          actualDF,\n          df\n        )\n      }\n    }\n\n    it(\"valid external host\") {\n\n      withObjectMocked[JdbcHelpers.type] {\n\n        setupMockJdbcHelper()\n        when(JdbcHelpers.getSinglestoreVersion(any[SinglestoreOptions])).thenReturn(\"7.1.0\")\n\n        val externalHostMap = Map(\n          \"172.17.0.2:3307\" -> \"172.17.0.2:3307\"\n        )\n        when(JdbcHelpers.externalHostPorts(any[SinglestoreOptions]))\n          .thenReturn(externalHostMap)\n\n        val actualDF =\n          spark.read\n            .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n            .option(\"useExternalHost\", \"true\")\n            .load(s\"$testDb.$testCollection\")\n\n        assertSmallDataFrameEquality(\n          actualDF,\n          df\n        )\n      }\n    }\n\n    it(\"empty external host map\") {\n\n      withObjectMocked[JdbcHelpers.type] {\n\n        setupMockJdbcHelper()\n        when(JdbcHelpers.getSinglestoreVersion(any[SinglestoreOptions])).thenReturn(\"7.1.0\")\n        when(JdbcHelpers.externalHostPorts(any[SinglestoreOptions]))\n          .thenReturn(Map.empty[String, String])\n\n        val actualDf = spark.read\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n          .option(\"useExternalHost\", \"true\")\n          .load(s\"$testDb.$testCollection\")\n\n        assertSmallDataFrameEquality(df, actualDf)\n      }\n    }\n\n    it(\"wrong external host map\") {\n\n      withObjectMocked[JdbcHelpers.type] {\n\n        setupMockJdbcHelper()\n        when(JdbcHelpers.getSinglestoreVersion(any[SinglestoreOptions])).thenReturn(\"7.1.0\")\n\n        val externalHostMap = Map(\n          \"172.17.0.3:3307\" -> \"172.17.0.100:3307\",\n          \"172.17.0.4:3307\" -> \"172.17.0.200:3307\"\n        )\n\n        when(JdbcHelpers.externalHostPorts(any[SinglestoreOptions]))\n          .thenReturn(externalHostMap)\n\n        val actualDf = spark.read\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n          .option(\"useExternalHost\", \"true\")\n          .load(s\"$testDb.$testCollection\")\n\n        assertSmallDataFrameEquality(df, actualDf)\n      }\n    }\n\n    it(\"valid external host function\") {\n\n      val mvNodesDf = spark.createDF(\n        List((\"172.17.0.2\", 3307, \"172.17.0.10\", 3310),\n             (\"172.17.0.20\", 3312, \"172.17.0.100\", null),\n             (\"172.17.0.2\", 3308, null, 3310),\n             (\"172.17.0.15\", 3311, null, null)),\n        List((\"IP_ADDR\", StringType, true),\n             (\"PORT\", IntegerType, true),\n             (\"EXTERNAL_HOST\", StringType, true),\n             (\"EXTERNAL_PORT\", IntegerType, true))\n      )\n      writeTable(s\"$testDb.$mvNodesCollection\", mvNodesDf)\n\n      val conf = new SinglestoreOptions(\n        s\"$masterHost:$masterPort\",\n        List.empty[String],\n        \"root\",\n        masterPassword,\n        None,\n        Map.empty[String, String],\n        false,\n        false,\n        Automatic,\n        List(ReadFromLeaves),\n        0,\n        0,\n        0,\n        0,\n        true,\n        Set.empty,\n        Truncate,\n        SinglestoreOptions.CompressionType.GZip,\n        SinglestoreOptions.LoadDataFormat.CSV,\n        List.empty[SinglestoreOptions.TableKey],\n        None,\n        10,\n        10,\n        false,\n        SinglestoreConnectionPoolOptions(enabled = true, -1, 8, 30000, 1000, -1, -1),\n        SinglestoreConnectionPoolOptions(enabled = true, -1, 8, 2000, 1000, -1, -1),\n        \"3.4.0\",\n        Option.empty\n      )\n\n      val conn =\n        SinglestoreConnectionPool.getConnection(getDDLConnProperties(conf, isOnExecutor = false))\n      val statement    = conn.prepareStatement(s\"\"\"\n        SELECT IP_ADDR,    \n        PORT,\n        EXTERNAL_HOST,         \n        EXTERNAL_PORT\n        FROM testdb.mv_nodes;\n      \"\"\")\n      val spyConn      = spy(conn)\n      val spyStatement = spy(statement)\n      when(spyConn.prepareStatement(s\"\"\"\n        SELECT IP_ADDR,    \n        PORT,\n        EXTERNAL_HOST,         \n        EXTERNAL_PORT\n        FROM INFORMATION_SCHEMA.MV_NODES \n        WHERE TYPE = \"LEAF\";\n      \"\"\")).thenReturn(spyStatement)\n\n      withObjectMocked[SinglestoreConnectionPool.type] {\n\n        when(SinglestoreConnectionPool.getConnection(any[Properties])).thenReturn(spyConn)\n        val externalHostPorts = JdbcHelpers.externalHostPorts(conf)\n        val expectedResult = Map(\n          \"172.17.0.2:3307\" -> \"172.17.0.10:3310\"\n        )\n        assert(externalHostPorts.equals(expectedResult))\n      }\n    }\n  }\n\n  describe(\"failed tests\") {\n\n    it(\"wrong external host\") {\n\n      withObjectMocked[JdbcHelpers.type] {\n\n        setupMockJdbcHelper()\n        when(JdbcHelpers.getSinglestoreVersion(any[SinglestoreOptions])).thenReturn(\"7.1.0\")\n\n        val externalHostMap = Map(\n          \"172.17.0.2:3307\" -> \"somehost:3307\"\n        )\n        when(JdbcHelpers.externalHostPorts(any[SinglestoreOptions]))\n          .thenReturn(externalHostMap)\n\n        try {\n          spark.read\n            .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n            .option(\"useExternalHost\", \"true\")\n            .option(\"enableParallelRead\", \"forced\")\n            .option(\"parallelRead.Features\", \"ReadFromLeaves\")\n            .load(s\"$testDb.$testCollection\")\n            .collect()\n          fail(\"Exception expected\")\n        } catch {\n          case ex: Throwable =>\n            ex match {\n              case sqlEx: ParallelReadFailedException =>\n                assert(\n                  sqlEx.getMessage startsWith \"Failed to read data in parallel.\\nTried following parallel read features:\")\n              case _ => fail(\"ParallelReadFailedException expected\")\n            }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/IntegrationSuiteBase.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Connection, DriverManager}\nimport java.util.{Properties, TimeZone}\n\nimport com.github.mrpowers.spark.fast.tests.DataFrameComparer\nimport org.apache.log4j.{Level, LogManager}\nimport org.apache.spark.sql.execution.datasources.jdbc.{JDBCOptions, JdbcUtils}\nimport org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}\nimport org.scalatest._\nimport org.scalatest.funspec.AnyFunSpec\nimport com.singlestore.spark.JdbcHelpers.executeQuery\nimport com.singlestore.spark.SQLGen.SinglestoreVersion\nimport com.singlestore.spark.SQLHelper._\n\nimport scala.util.Random\n\ntrait IntegrationSuiteBase\n    extends AnyFunSpec\n    with BeforeAndAfterEach\n    with BeforeAndAfterAll\n    with DataFrameComparer\n    with LazyLogging {\n  object ExcludeFromSpark40 extends Tag(\"ExcludeFromSpark40\")\n  object ExcludeFromSpark35 extends Tag(\"ExcludeFromSpark35\")\n  object ExcludeFromSpark34 extends Tag(\"ExcludeFromSpark34\")\n  object ExcludeFromSpark33 extends Tag(\"ExcludeFromSpark33\")\n  object ExcludeFromSpark32 extends Tag(\"ExcludeFromSpark32\")\n  object ExcludeFromSpark31 extends Tag(\"ExcludeFromSpark31\")\n\n  final val masterHost: String = sys.props.getOrElse(\"singlestore.host\", \"localhost\")\n  final val masterPort: String = sys.props.getOrElse(\"singlestore.port\", \"5506\")\n\n  final val continuousIntegration: Boolean = sys.env\n    .getOrElse(\"CONTINUOUS_INTEGRATION\", \"false\") == \"true\"\n\n  final val masterPassword: String    = sys.env.getOrElse(\"SINGLESTORE_PASSWORD\", \"1\")\n  final val masterJWTPassword: String = sys.env.getOrElse(\"SINGLESTORE_JWT_PASSWORD\", \"\")\n  final val forceReadFromLeaves: Boolean =\n    sys.env.getOrElse(\"FORCE_READ_FROM_LEAVES\", \"FALSE\").equalsIgnoreCase(\"TRUE\")\n\n  var spark: SparkSession = _\n\n  val jdbcDefaultProps = new Properties()\n  jdbcDefaultProps.setProperty(JDBCOptions.JDBC_TABLE_NAME, \"XXX\")\n  jdbcDefaultProps.setProperty(JDBCOptions.JDBC_DRIVER_CLASS, \"org.mariadb.jdbc.Driver\")\n  jdbcDefaultProps.setProperty(\"user\", \"root\")\n  jdbcDefaultProps.setProperty(\"password\", masterPassword)\n\n  val version: SinglestoreVersion = {\n    val conn =\n      DriverManager.getConnection(s\"jdbc:mysql://$masterHost:$masterPort\", jdbcDefaultProps)\n    val resultSet = executeQuery(conn, \"select @@memsql_version\")\n    SinglestoreVersion(resultSet.next().getString(0))\n  }\n\n  val canDoParallelReadFromAggregators: Boolean = version.atLeast(\"7.5.0\") && !forceReadFromLeaves\n\n  override def beforeAll(): Unit = {\n    // override global JVM timezone to GMT\n    TimeZone.setDefault(TimeZone.getTimeZone(\"GMT\"))\n\n    val conn =\n      DriverManager.getConnection(s\"jdbc:mysql://$masterHost:$masterPort\", jdbcDefaultProps)\n    try {\n      // make singlestore use less memory\n      executeQuery(conn, \"set global default_partitions_per_leaf = 2\")\n      executeQuery(conn, \"set global data_conversion_compatibility_level = '6.0'\")\n\n      executeQuery(conn, \"drop database if exists testdb\")\n      executeQuery(conn, \"create database testdb\")\n    } finally {\n      conn.close()\n    }\n  }\n\n  override def withFixture(test: NoArgTest): Outcome = {\n    def retryThrowable(t: Throwable): Boolean = t match {\n      case _: java.sql.SQLNonTransientConnectionException => true\n      case _                                              => false\n    }\n\n    @scala.annotation.tailrec\n    def runWithRetry(attempts: Int, lastError: Option[Throwable]): Outcome = {\n      if (attempts == 0) {\n        return Canceled(\n          s\"too many SQLNonTransientConnectionExceptions occurred, last error was:\\n${lastError.get}\")\n      }\n\n      super.withFixture(test) match {\n        case Failed(t: Throwable) if retryThrowable(t) || retryThrowable(t.getCause) => {\n          Thread.sleep(3000)\n          runWithRetry(attempts - 1, Some(t))\n        }\n        case other => other\n      }\n    }\n\n    runWithRetry(attempts = 5, None)\n  }\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n\n    val seed = Random.nextLong()\n    log.debug(\"Random seed: \" + seed)\n    Random.setSeed(seed)\n\n    if (!continuousIntegration) {\n      LogManager.getLogger(\"com.singlestore.spark\").setLevel(Level.TRACE)\n    }\n\n    spark = SparkSession\n      .builder()\n      .master(if (canDoParallelReadFromAggregators) \"local[2]\" else \"local\")\n      .appName(\"singlestore-integration-tests\")\n      .config(\"spark.driver.host\", \"127.0.0.1\")\n      .config(\"spark.sql.ansi.enabled\", \"false\")\n      .config(\"spark.driver.bindAddress\", \"127.0.0.1\")\n      .config(\"spark.driver.blockManager.port\", \"0\")\n      .config(\"spark.driver.port\", \"0\")\n      .config(\"spark.sql.shuffle.partitions\", \"1\")\n      .config(\"spark.driver.bindAddress\", \"localhost\")\n      .config(\"spark.driver.extraJavaOptions\", \"-Duser.timezone=GMT\")\n      .config(\"spark.executor.extraJavaOptions\", \"-Duser.timezone=GMT\")\n      .config(\"spark.sql.session.timeZone\", \"GMT\")\n      .config(\"spark.datasource.singlestore.ddlEndpoint\", s\"${masterHost}:${masterPort}\")\n      .config(\"spark.datasource.singlestore.user\", \"root-ssl\")\n      .config(\"spark.datasource.singlestore.password\", \"\")\n      .config(\"spark.datasource.singlestore.enableAsserts\", \"true\")\n      .config(\"spark.datasource.singlestore.enableParallelRead\", \"automaticLite\")\n      .config(\"spark.datasource.singlestore.parallelRead.Features\",\n              if (forceReadFromLeaves) \"ReadFromLeaves\" else \"ReadFromAggregators,ReadFromLeaves\")\n      .config(\"spark.datasource.singlestore.database\", \"testdb\")\n      .config(\"spark.datasource.singlestore.useSSL\", \"true\")\n      .config(\"spark.datasource.singlestore.serverSslCert\",\n              s\"${System.getProperty(\"user.dir\")}/scripts/ssl/ca-cert.pem\")\n      .config(\"spark.datasource.singlestore.disableSslHostnameVerification\", \"true\")\n      .config(\"spark.sql.crossJoin.enabled\", \"true\")\n      .getOrCreate()\n  }\n\n  override def afterEach(): Unit = {\n    super.afterEach()\n    spark.close()\n  }\n\n  def executeQueryWithLog(sql: String): Unit = {\n    log.trace(s\"executing query: ${sql}\")\n    spark.executeSinglestoreQuery(sql)\n  }\n\n  def jdbcOptions(dbtable: String): Map[String, String] = Map(\n    \"url\"               -> s\"jdbc:mysql://$masterHost:$masterPort\",\n    \"dbtable\"           -> dbtable,\n    \"user\"              -> \"root\",\n    \"password\"          -> masterPassword,\n    \"pushDownPredicate\" -> \"false\"\n  )\n\n  def jdbcOptionsSQL(dbtable: String): String =\n    jdbcOptions(dbtable)\n      .foldLeft(List.empty[String])({\n        case (out, (k, v)) => s\"'${k}'='${v}'\" :: out\n      })\n      .mkString(\", \")\n\n  def writeTable(dbtable: String, df: DataFrame, saveMode: SaveMode = SaveMode.Overwrite): Unit =\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(saveMode)\n      .save(dbtable)\n\n  def insertValues(dbtable: String,\n                   df: DataFrame,\n                   onDuplicateKeySQL: String,\n                   insertBatchSize: Long): Unit =\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"onDuplicateKeySQL\", onDuplicateKeySQL)\n      .option(\"insertBatchSize\", insertBatchSize)\n      .mode(SaveMode.Append)\n      .save(dbtable)\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/IssuesTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.SaveMode\nimport org.apache.spark.sql.functions._\nimport org.apache.spark.sql.types._\n\nclass IssuesTest extends IntegrationSuiteBase {\n  it(\"https://github.com/memsql/singlestore-spark-connector/issues/41\") {\n    executeQueryWithLog(\"\"\"\n        | create table if not exists testdb.issue41 (\n        |   start_video_pos smallint(5) unsigned DEFAULT NULL\n        | )\n        |\"\"\".stripMargin)\n\n    val df = spark.createDF(\n      List(1.toShort, 2.toShort, 3.toShort, 4.toShort),\n      List((\"start_video_pos\", ShortType, true))\n    )\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(SaveMode.Append)\n      .save(\"issue41\")\n\n    val df2 = spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"issue41\")\n    assertSmallDataFrameEquality(df2,\n                                 spark.createDF(\n                                   List(1, 2, 3, 4),\n                                   List((\"start_video_pos\", IntegerType, true))\n                                 ),\n                                 orderedComparison = false)\n  }\n\n  it(\"https://memsql.zendesk.com/agent/tickets/10451\") {\n    // parallel read should support columnar scan with filter\n    executeQueryWithLog(\"\"\"\n      | create table if not exists testdb.ticket10451 (\n      |   t text,\n      |   h bigint(20) DEFAULT NULL,\n      |   KEY h (h) USING CLUSTERED COLUMNSTORE\n      | )\n      | \"\"\".stripMargin)\n\n    val df = spark.createDF(\n      List((\"hi\", 2L), (\"hi\", 3L), (\"foo\", 4L)),\n      List((\"t\", StringType, true), (\"h\", LongType, true))\n    )\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(SaveMode.Append)\n      .save(\"ticket10451\")\n\n    val df2 = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .load(\"ticket10451\")\n      .where(col(\"t\") === \"hi\")\n      .where(col(\"h\") === 3L)\n\n    assert(df2.rdd.getNumPartitions > 1)\n    assertSmallDataFrameEquality(df2,\n                                 spark.createDF(\n                                   List((\"hi\", 3L)),\n                                   List((\"t\", StringType, true), (\"h\", LongType, true))\n                                 ))\n  }\n\n  it(\"supports reading count from query\") {\n    val df = spark.createDF(\n      List((1, \"Albert\"), (5, \"Ronny\"), (7, \"Ben\"), (9, \"David\")),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true))\n    )\n    writeTable(\"testdb.testcount\", df)\n    val data = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"query\", \"select count(1) from testcount where id > 1 \")\n      .option(\"database\", \"testdb\")\n      .load()\n      .collect()\n    val count = data.head.getLong(0)\n    assert(count == 3)\n  }\n\n  it(\"handles exceptions raised by asCode\") {\n    // in certain cases asCode will raise NullPointerException due to this bug\n    // https://issues.apache.org/jira/browse/SPARK-31403\n    writeTable(\"testdb.nulltest\",\n               spark.createDF(\n                 List(1, null),\n                 List((\"i\", IntegerType, true))\n               ))\n    spark.sql(s\"create table nulltest using singlestore options ('dbtable'='testdb.nulltest')\")\n\n    val df2 = spark.sql(\"select if(isnull(i), null, 2) as x from nulltest order by i\")\n\n    assertSmallDataFrameEquality(df2,\n                                 spark.createDF(\n                                   List(null, 2),\n                                   List((\"x\", IntegerType, true))\n                                 ))\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/LoadDataBenchmark.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Connection, Date, DriverManager}\nimport java.time.{Instant, LocalDate}\nimport java.util.Properties\n\nimport org.apache.spark.sql.types._\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.{SaveMode, SparkSession}\n\nimport scala.util.Random\n\n// LoadDataBenchmark is written to test load data with CPU profiler\n// this feature is accessible in Ultimate version of IntelliJ IDEA\n// see https://www.jetbrains.com/help/idea/async-profiler.html#profile for more details\nobject LoadDataBenchmark extends App {\n  final val masterHost: String = sys.props.getOrElse(\"singlestore.host\", \"localhost\")\n  final val masterPort: String = sys.props.getOrElse(\"singlestore.port\", \"5506\")\n\n  val spark: SparkSession = SparkSession\n    .builder()\n    .master(\"local\")\n    .config(\"spark.sql.shuffle.partitions\", \"1\")\n    .config(\"spark.driver.bindAddress\", \"localhost\")\n    .config(\"spark.datasource.singlestore.ddlEndpoint\", s\"${masterHost}:${masterPort}\")\n    .config(\"spark.datasource.singlestore.database\", \"testdb\")\n    .getOrCreate()\n\n  def jdbcConnection: Loan[Connection] = {\n    val connProperties = new Properties()\n    connProperties.put(\"user\", \"root\")\n\n    Loan(\n      DriverManager.getConnection(\n        s\"jdbc:singlestore://$masterHost:$masterPort\",\n        connProperties\n      ))\n  }\n\n  def executeQuery(sql: String): Unit = {\n    jdbcConnection.to(conn => Loan(conn.createStatement).to(_.execute(sql)))\n  }\n\n  executeQuery(\"set global default_partitions_per_leaf = 2\")\n  executeQuery(\"drop database if exists testdb\")\n  executeQuery(\"create database testdb\")\n\n  def genRow(): (Long, Int, Double, String) =\n    (Random.nextLong(), Random.nextInt(), Random.nextDouble(), Random.nextString(20))\n  val df =\n    spark.createDF(\n      List.fill(1000000)(genRow()),\n      List((\"LongType\", LongType, true),\n           (\"IntType\", IntegerType, true),\n           (\"DoubleType\", DoubleType, true),\n           (\"StringType\", StringType, true))\n    )\n\n  val start = System.nanoTime()\n  df.write\n    .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n    .mode(SaveMode.Append)\n    .save(\"testdb.batchinsert\")\n\n  val diff = System.nanoTime() - start\n  println(\"Elapsed time: \" + diff + \"ns [CSV serialization] \")\n\n  executeQuery(\"truncate testdb.batchinsert\")\n\n  val avroStart = System.nanoTime()\n  df.write\n    .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n    .mode(SaveMode.Append)\n    .option(SinglestoreOptions.LOAD_DATA_FORMAT, \"Avro\")\n    .save(\"testdb.batchinsert\")\n  val avroDiff = System.nanoTime() - avroStart\n  println(\"Elapsed time: \" + avroDiff + \"ns [Avro serialization] \")\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/LoadDataTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.types.{DecimalType, IntegerType, NullType, StringType}\nimport org.apache.spark.sql.{DataFrame, SaveMode}\nimport org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}\n\nimport scala.util.Try\n\nclass LoadDataTest extends IntegrationSuiteBase with BeforeAndAfterEach with BeforeAndAfterAll {\n  var df: DataFrame = _\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n\n    df = spark.createDF(\n      List(),\n      List((\"id\", IntegerType, false), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n\n    writeTable(\"testdb.loaddata\", df)\n  }\n\n  it(\"appends row without `age` field\") {\n    df = spark.createDF(\n      List((2, \"B\")),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true))\n    )\n    writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark.createDF(\n        List((2, \"B\", null)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      ))\n  }\n\n  it(\"appends row without `name` field\") {\n    df = spark.createDF(\n      List((3, 30)),\n      List((\"id\", IntegerType, true), (\"age\", IntegerType, true))\n    )\n    writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark.createDF(\n        List((3, null, 30)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      ))\n  }\n\n  it(\"should not append row without not nullable `id` field\") {\n    df = spark.createDF(\n      List((\"D\", 40)),\n      List((\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n\n    try {\n      writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n      fail()\n    } catch {\n      // error code 1364 is `Field 'id' doesn't have a default value`\n      case e: Throwable if TestHelper.isSQLExceptionWithCode(e, List(1364)) =>\n    }\n  }\n\n  it(\"appends row with all fields\") {\n    df = spark.createDF(\n      List((5, \"E\", 50)),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n    writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark.createDF(\n        List((5, \"E\", 50)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      ))\n  }\n\n  it(\"appends row only with `id` field\") {\n    df = spark.createDF(List(6), List((\"id\", IntegerType, true)))\n    writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark.createDF(\n        List((6, null, null)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      )\n    )\n  }\n\n  it(\"appends row with all fields in wrong order\") {\n    df = spark.createDF(\n      List((\"WO\", 101, 101)),\n      List((\"name\", StringType, true), (\"age\", IntegerType, true), (\"id\", IntegerType, true))\n    )\n    writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark.createDF(\n        List((101, \"WO\", 101)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      ))\n  }\n\n  it(\"appends row with `id` and `name` fields in wrong order\") {\n    df = spark.createDF(\n      List((\"WO2\", 102)),\n      List((\"name\", StringType, true), (\"id\", IntegerType, true))\n    )\n    writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark.createDF(\n        List((102, \"WO2\", null)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      )\n    )\n  }\n\n  it(\"should not append row with more fields than expected\") {\n    df = spark.createDF(\n      List((1, \"1\", 1, 1)),\n      List((\"id\", IntegerType, true),\n           (\"name\", StringType, true),\n           (\"age\", IntegerType, true),\n           (\"extra\", IntegerType, false))\n    )\n\n    try {\n      writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n      fail()\n    } catch {\n      // error code 1054 is `Unknown column 'extra' in 'field list'`\n      case e: Throwable if TestHelper.isSQLExceptionWithCode(e, List(1054)) =>\n    }\n  }\n\n  it(\"should not append row with wrong field name\") {\n    df = spark.createDF(\n      List((1, \"1\", 1)),\n      List((\"id\", IntegerType, true), (\"wrongname\", StringType, true), (\"age\", IntegerType, true)))\n\n    try {\n      writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n      fail()\n    } catch {\n      // error code 1054 is `Unknown column 'wrongname' in 'field list'`\n      case e: Throwable if TestHelper.isSQLExceptionWithCode(e, List(1054)) =>\n    }\n  }\n\n  it(\"should fail creating table with NullType\") {\n    val tableName = \"null_type\"\n\n    val dfNull = spark.createDF(List(null), List((\"id\", NullType, true)))\n    val writeResult = Try {\n      writeTable(s\"testdb.$tableName\", dfNull, SaveMode.Append)\n    }\n    assert(writeResult.isFailure)\n    assert(\n      writeResult.failed.get.getMessage\n        .equals(\n          \"No corresponding SingleStore type found for NullType. If you want to use NullType, please write to an already existing SingleStore table.\"))\n  }\n\n  it(\"should succeed inserting NullType in existed table\") {\n    val tableName = \"null_type\"\n\n    df = spark.createDF(List(1, 2, 3, 4, 5), List((\"id\", IntegerType, true)))\n    writeTable(s\"testdb.$tableName\", df, SaveMode.Append)\n\n    val dfNull = spark.createDF(List(null), List((\"id\", NullType, true)))\n    writeTable(s\"testdb.$tableName\", dfNull, SaveMode.Append)\n  }\n\n  it(\"should write BigDecimal with Avro serializing\") {\n    val tableName = \"bigDecimalAvro\"\n    val df = spark.createDF(\n      List((1, \"Alice\", 213: BigDecimal)),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", DecimalType(10, 0), true))\n    )\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(SaveMode.Overwrite)\n      .option(SinglestoreOptions.LOAD_DATA_FORMAT, \"avro\")\n      .save(s\"testdb.$tableName\")\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(s\"testdb.$tableName\")\n    assertLargeDataFrameEquality(actualDF, df)\n  }\n\n  it(\"should work with `memsql` and `com.memsql.spark` source\") {\n    df = spark.createDF(\n      List((5, \"E\", 50)),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n    writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n\n    val actualDFShort =\n      spark.read.format(DefaultSource.MEMSQL_SOURCE_NAME_SHORT).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDFShort,\n      spark.createDF(\n        List((5, \"E\", 50)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      ))\n    val actualDF =\n      spark.read.format(DefaultSource.MEMSQL_SOURCE_NAME).load(\"testdb.loaddata\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      spark.createDF(\n        List((5, \"E\", 50)),\n        List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n      ))\n  }\n\n  it(\"non-existing column\") {\n    executeQueryWithLog(\"DROP TABLE IF EXISTS loaddata\")\n    executeQueryWithLog(\"CREATE TABLE loaddata(id INT, name TEXT)\")\n\n    df = spark.createDF(\n      List((5, \"EBCEFGRHFED\" * 10000000, 50)),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n\n    try {\n      writeTable(\"testdb.loaddata\", df, SaveMode.Append)\n      fail()\n    } catch {\n      case e: Exception if e.getMessage.contains(\"Unknown column 'age' in 'field list'\") =>\n    }\n  }\n\n  it(\"load data in batches\") {\n    val df1 = spark.createDF(\n      List(\n        (5, \"Jack\", 20),\n        (6, \"Mark\", 30),\n        (7, \"Fred\", 15),\n        (8, \"Jany\", 40),\n        (9, \"Monica\", 5)\n      ),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n    val df2 = spark.createDF(\n      List(\n        (10, \"Jany\", 40),\n        (11, \"Monica\", 5)\n      ),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n    val df3 = spark.createDF(\n      List(),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"age\", IntegerType, true))\n    )\n\n    df1.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"insertBatchSize\", 2)\n      .mode(SaveMode.Append)\n      .save(\"testdb.loadDataBatches\")\n    df2.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"insertBatchSize\", 2)\n      .mode(SaveMode.Append)\n      .save(\"testdb.loadDataBatches\")\n    df3.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"insertBatchSize\", 2)\n      .mode(SaveMode.Append)\n      .save(\"testdb.loadDataBatches\")\n\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(\"testdb.loadDataBatches\")\n    assertSmallDataFrameEquality(\n      actualDF,\n      df1.union(df2).union(df3),\n      orderedComparison = false\n    )\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/LoadbalanceTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.JdbcHelpers.executeQuery\nimport org.apache.spark.sql.SaveMode\nimport org.apache.spark.sql.execution.datasources.jdbc.{JDBCOptions, JdbcUtils}\nimport org.apache.spark.sql.jdbc.JdbcDialects\nimport org.apache.spark.sql.types.IntegerType\n\nimport java.sql.DriverManager\nimport java.util.Properties\n\nclass LoadbalanceTest extends IntegrationSuiteBase {\n\n  val masterHostPort = s\"${masterHost}:${masterPort}\"\n  val childHostPort  = \"localhost:5508\"\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n\n    // Set master + child aggregator as dmlEndpoints\n    spark.conf\n      .set(\"spark.datasource.singlestore.dmlEndpoints\", s\"${masterHostPort},${childHostPort}\")\n  }\n\n  def countQueries(hostport: String): Int = {\n    val props = new Properties()\n    props.setProperty(\"dbtable\", \"testdb\")\n    props.setProperty(\"user\", \"root\")\n    props.setProperty(\"password\", masterPassword)\n\n    val conn = DriverManager.getConnection(s\"jdbc:singlestore://$hostport\", props)\n    try {\n      // we only use write queries since read queries are always increasing due to internal status checks\n      val rows =\n        JdbcHelpers.executeQuery(conn, \"show status extended like 'Successful_write_queries'\")\n      rows.map(r => r.getAs[String](1).toInt).sum\n    } finally {\n      conn.close()\n    }\n  }\n\n  def counters =\n    Map(\n      masterHostPort -> countQueries(masterHostPort),\n      childHostPort  -> countQueries(childHostPort)\n    )\n\n  describe(\"load-balances among all hosts listed in dmlEndpoints\") {\n\n    it(\"queries both aggregators eventually\") {\n\n      val df = spark.createDF(\n        List(4, 5, 6),\n        List((\"id\", IntegerType, true))\n      )\n\n      val startCounters = counters\n\n      // 50/50 chance of picking either agg, 10 tries should be enough to ensure we hit both aggs with write queries\n      for (i <- 0 to 10) {\n        df.write\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n          .option(\"driverConnectionPool.MinEvictableIdleTimeMs\", \"100\")\n          .option(\"driverConnectionPool.TimeBetweenEvictionRunsMS\", \"50\")\n          .option(\"executorConnectionPool.MinEvictableIdleTimeMs\", \"100\")\n          .option(\"executorConnectionPool.TimeBetweenEvictionRunsMS\", \"50\")\n          .mode(SaveMode.Overwrite)\n          .save(\"test\")\n\n        Thread.sleep(300)\n      }\n\n      val endCounters = counters\n\n      assert(endCounters(childHostPort) > startCounters(childHostPort))\n      assert(endCounters(masterHostPort) > startCounters(masterHostPort))\n    }\n\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/MaxErrorsTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.SQLTransientConnectionException\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.SaveMode\nimport org.apache.spark.sql.types.{DecimalType, IntegerType, StringType}\nimport org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}\n\nimport scala.util.Try\n\nclass MaxErrorsTest extends IntegrationSuiteBase with BeforeAndAfterEach with BeforeAndAfterAll {\n\n  def testMaxErrors(tableName: String, maxErrors: Int, duplicateItems: Int): Unit = {\n    val df = spark.createDF(\n      List.fill(duplicateItems + 1)((1, \"Alice\", 213: BigDecimal)),\n      List((\"id\", IntegerType, true),\n           (\"name\", StringType, false),\n           (\"age\", DecimalType(10, 0), true))\n    )\n    val result = Try {\n      df.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .option(\"tableKey.primary\", \"name\")\n        .option(\"maxErrors\", maxErrors)\n        .mode(SaveMode.Ignore)\n        .save(s\"testdb.$tableName\")\n    }\n    if (duplicateItems > maxErrors) {\n      assert(result.isFailure)\n      result.failed.get.getCause match {\n        case _: SQLTransientConnectionException =>\n        case _                                  => fail(\"SQLTransientConnectionException should be thrown\")\n      }\n    } else {\n      assert(result.isSuccess)\n    }\n  }\n\n  describe(\"small dataset\") {\n    // TODO DB-51213\n    //it(\"hit maxErrors\") {\n    //  testMaxErrors(\"hitMaxErrorsSmall\", 1, 2)\n    //}\n\n    it(\"not hit maxErrors\") {\n      testMaxErrors(\"notHitMaxErrorsSmall\", 1, 1)\n    }\n  }\n\n  describe(\"big dataset\") {\n    // TODO DB-51213\n    //it(\"hit maxErrors\") {\n    //  testMaxErrors(\"hitMaxErrorsBig\", 10000, 10001)\n    //}\n\n    it(\"not hit maxErrors\") {\n      testMaxErrors(\"notHitMaxErrorsBig\", 10000, 10000)\n    }\n  }\n\n  it(\"wrong configuration\") {\n    val df = spark.createDF(\n      List((1, \"Alice\", 213: BigDecimal)),\n      List((\"id\", IntegerType, true),\n           (\"name\", StringType, false),\n           (\"age\", DecimalType(10, 0), true))\n    )\n    val result = Try {\n      df.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .option(\"onDuplicateKeySQL\", \"id=id\")\n        .option(\"maxErrors\", 1)\n        .mode(SaveMode.Ignore)\n        .save(s\"testdb.someTable\")\n    }\n    assert(result.isFailure)\n    result.failed.get match {\n      case ex: IllegalArgumentException\n          if ex.getMessage.equals(\"can't use both `onDuplicateKeySQL` and `maxErrors` options\") =>\n        succeed\n      case _ => fail()\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/OutputMetricsTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.util.concurrent.CountDownLatch\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.scheduler.{SparkListener, SparkListenerTaskEnd}\nimport org.apache.spark.sql.types.{IntegerType, StringType}\n\nclass OutputMetricsTest extends IntegrationSuiteBase {\n  it(\"records written\") {\n    var outputWritten                  = 0L\n    var countDownLatch: CountDownLatch = null\n    spark.sparkContext.addSparkListener(new SparkListener() {\n      override def onTaskEnd(taskEnd: SparkListenerTaskEnd) {\n        if (taskEnd.taskType == \"ResultTask\") {\n          outputWritten.synchronized({\n            val metrics = taskEnd.taskMetrics\n            outputWritten += metrics.outputMetrics.recordsWritten\n            countDownLatch.countDown()\n          })\n        }\n      }\n    })\n\n    val numRows = 100000\n    var df1 = spark.createDF(\n      List.range(0, numRows),\n      List((\"id\", IntegerType, true))\n    )\n\n    var numPartitions = 30\n    countDownLatch = new CountDownLatch(numPartitions)\n    df1 = df1.repartition(numPartitions)\n\n    df1.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .save(\"metricsInts\")\n\n    countDownLatch.await()\n    assert(outputWritten == numRows)\n\n    var df2 = spark.createDF(\n      List(\"st1\", \"\", null),\n      List((\"st\", StringType, true))\n    )\n\n    outputWritten = 0\n    numPartitions = 1\n    countDownLatch = new CountDownLatch(numPartitions)\n    df2 = df2.repartition(numPartitions)\n\n    df2.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .save(\"metricsStrings\")\n    countDownLatch.await()\n    assert(outputWritten == 3)\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/ReferenceTableTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport org.apache.spark.sql.types.IntegerType\nimport org.apache.spark.sql.{DataFrame, SaveMode}\n\nimport scala.util.Try\n\nclass ReferenceTableTest extends IntegrationSuiteBase {\n\n  val childAggregatorHost = \"localhost\"\n  val childAggregatorPort = \"5508\"\n\n  val dbName                  = \"testdb\"\n  val commonCollectionName    = \"test_table\"\n  val referenceCollectionName = \"reference_table\"\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n\n    // Set child aggregator as a dmlEndpoint\n    spark.conf\n      .set(\"spark.datasource.singlestore.dmlEndpoints\",\n           s\"${childAggregatorHost}:${childAggregatorPort}\")\n  }\n\n  def writeToTable(tableName: String): Unit = {\n    val df = spark.createDF(\n      List(4, 5, 6),\n      List((\"id\", IntegerType, true))\n    )\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(SaveMode.Append)\n      .save(s\"${dbName}.${tableName}\")\n  }\n\n  def readFromTable(tableName: String): DataFrame = {\n    spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .load(s\"${dbName}.${tableName}\")\n  }\n\n  def writeAndReadFromTable(tableName: String): Unit = {\n    writeToTable(tableName)\n    val dataFrame = readFromTable(tableName)\n    val sqlRows   = dataFrame.collect();\n    assert(sqlRows.length == 3)\n  }\n\n  def dropTable(tableName: String): Unit =\n    executeQueryWithLog(s\"drop table if exists $dbName.$tableName\")\n\n  describe(\"Success during write operations\") {\n\n    it(\"to common table\") {\n      dropTable(commonCollectionName)\n      executeQueryWithLog(\n        s\"create table if not exists $dbName.$commonCollectionName (id INT NOT NULL, PRIMARY KEY (id))\")\n      writeAndReadFromTable(commonCollectionName)\n    }\n\n    it(\"to reference table\") {\n      dropTable(referenceCollectionName)\n      executeQueryWithLog(\n        s\"create reference table if not exists $dbName.$referenceCollectionName (id INT NOT NULL, PRIMARY KEY (id))\")\n      writeAndReadFromTable(referenceCollectionName)\n    }\n  }\n\n  describe(\"Success during creating\") {\n\n    it(\"common table\") {\n      dropTable(commonCollectionName)\n      writeAndReadFromTable(commonCollectionName)\n    }\n  }\n\n  describe(\"Failure because of\") {\n\n    it(\"database name not specified\") {\n      spark.conf.set(\"spark.datasource.singlestore.database\", \"\")\n      val df = spark.createDF(\n        List(4, 5, 6),\n        List((\"id\", IntegerType, true))\n      )\n      val result = Try {\n        df.write\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n          .mode(SaveMode.Append)\n          .save(s\"${commonCollectionName}\")\n      }\n      /* Error code description:\n        1046 = Database name not provided\n       * */\n      assert(TestHelper.isSQLExceptionWithCode(result.failed.get, List(1046)))\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/SQLHelperTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.{Date, Timestamp}\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.SQLHelper._\nimport org.apache.spark.sql.DataFrame\nimport org.apache.spark.sql.types._\nimport org.scalatest.BeforeAndAfterEach\n\nclass SQLHelperTest extends IntegrationSuiteBase with BeforeAndAfterEach {\n  var df: DataFrame = _\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n    df = spark.createDF(\n      List((1, \"Cat\", true), (2, \"Dog\", true), (3, \"CatDog\", false)),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true), (\"domestic\", BooleanType, true))\n    )\n    writeTable(\"testdb.animal\", df)\n\n    df = spark.createDF(\n      List((1: Long, 2: Short, 3: Float, 4: Double),\n           (-2: Long, 22: Short, 2.0.toFloat, 5.1: Double),\n           (3: Long, 4: Short, -0.11.toFloat, 66.77: Double)),\n      List((\"nLong\", LongType, true),\n           (\"nShort\", ShortType, true),\n           (\"nFloat\", FloatType, true),\n           (\"nDouble\", DoubleType, true))\n    )\n    writeTable(\"testdb.numbers\", df)\n\n    df = spark.createDF(\n      List((1: Byte, Date.valueOf(\"2015-03-30\"), new Timestamp(2147483649L)),\n           (2: Byte, Date.valueOf(\"2020-09-09\"), new Timestamp(1000))),\n      List((\"nByte\", ByteType, true),\n           (\"nDate\", DateType, true),\n           (\"nTimestamp\", TimestampType, true))\n    )\n    writeTable(\"testdb.byte_dates\", df)\n  }\n\n  override def afterEach(): Unit = {\n    super.afterEach()\n\n    spark.executeSinglestoreQueryDB(\"testdb\", \"DROP TABLE IF EXISTS animal\")\n    spark.executeSinglestoreQueryDB(\"testdb\", \"DROP TABLE IF EXISTS numbers\")\n    spark.executeSinglestoreQueryDB(\"testdb\", \"DROP TABLE IF EXISTS byte_dates\")\n    spark.executeSinglestoreQuery(\"DROP DATABASE IF EXISTS test_db_1\")\n  }\n\n  describe(\"implicit version\") {\n    it(\"global query test\") {\n      val s = spark.executeSinglestoreQuery(\"SHOW DATABASES\")\n      val result =\n        (for (row <- s)\n          yield row.getString(0)).toList\n      for (db <- List(\"memsql\", \"cluster\", \"testdb\", \"information_schema\")) {\n        assert(result.contains(db))\n      }\n    }\n\n    it(\"executeSinglestoreQuery with 2 parameters\") {\n      spark.executeSinglestoreQuery(\"DROP DATABASE IF EXISTS test_db_1\")\n      spark.executeSinglestoreQuery(\"CREATE DATABASE test_db_1\")\n    }\n\n    it(\"executeSinglestoreQueryDB with 3 parameters\") {\n      val res = spark.executeSinglestoreQueryDB(\"testdb\", \"SELECT * FROM animal\")\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList.sorted == List(\"Cat\", \"CatDog\", \"Dog\"))\n    }\n\n    it(\"executeSingleStoreQuery without explicitly specified db\") {\n      val res = spark.executeSinglestoreQuery(\"SELECT * FROM animal\")\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList.sorted == List(\"Cat\", \"CatDog\", \"Dog\"))\n    }\n\n    it(\"executeSinglestoreQuery with query params\") {\n      val params = List(\"%Cat%\", 1, false)\n      val res = spark.executeSinglestoreQuery(\n        \"SELECT * FROM animal WHERE name LIKE ? AND id > ? AND domestic = ?\",\n        \"%Cat%\",\n        1,\n        false)\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList == List(\"CatDog\"))\n    }\n\n    it(\"executeSinglestoreQuery with db different from the one found in SparkContext\") {\n      spark.executeSinglestoreQuery(query = \"CREATE DATABASE test_db_1\")\n      spark.executeSinglestoreQuery(query = \"CREATE TABLE test_db_1.animal (id INT, name TEXT)\")\n\n      val res = spark.executeSinglestoreQueryDB(\"test_db_1\", \"SELECT * FROM animal\")\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList == List())\n    }\n  }\n\n  it(\"executeSinglestoreQuery with numeric columns\") {\n    val params = List(1, 22, 2, null)\n    val res = spark.executeSinglestoreQuery(\n      \"SELECT * FROM numbers WHERE nLong = ? OR nShort = ? OR nFloat = ? OR nDouble = ?\",\n      1,\n      22,\n      2,\n      null)\n    assert(res.length == 2)\n  }\n\n  it(\"executeSinglestoreQuery with byte and date columns\") {\n    val res = spark.executeSinglestoreQuery(\n      \"SELECT * FROM byte_dates WHERE nByte = ? OR nDate = ? OR nTimestamp = ?\",\n      2,\n      \"2015-03-30\",\n      2000)\n    assert(res.length == 2)\n  }\n\n  describe(\"explicit version\") {\n    it(\"global query test\") {\n      val s = executeSinglestoreQuery(spark, \"SHOW DATABASES\")\n      val result =\n        (for (row <- s)\n          yield row.getString(0)).toList\n      for (db <- List(\"memsql\", \"cluster\", \"testdb\", \"information_schema\")) {\n        assert(result.contains(db))\n      }\n    }\n\n    it(\"executeSinglestoreQuery with 2 parameters\") {\n      executeSinglestoreQuery(spark, \"DROP DATABASE IF EXISTS test_db_1\")\n      executeSinglestoreQuery(spark, \"CREATE DATABASE test_db_1\")\n    }\n\n    it(\"executeSinglestoreQueryDB with 3 parameters\") {\n      val res = executeSinglestoreQueryDB(spark, \"testdb\", \"SELECT * FROM animal\")\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList.sorted == List(\"Cat\", \"CatDog\", \"Dog\"))\n    }\n\n    it(\"executeSinglestoreQuery without explicitly specified db\") {\n      val res = executeSinglestoreQuery(spark, \"SELECT * FROM animal\")\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList.sorted == List(\"Cat\", \"CatDog\", \"Dog\"))\n    }\n\n    it(\"executeSinglestoreQuery with query params\") {\n      val params = List(\"%Cat%\", 1, false)\n      val res =\n        executeSinglestoreQuery(\n          spark,\n          \"SELECT * FROM animal WHERE name LIKE ? AND id > ? AND domestic = ?\",\n          \"%Cat%\",\n          1,\n          false)\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList == List(\"CatDog\"))\n    }\n\n    it(\"executeSinglestoreQuery with db different from the one found in SparkContext\") {\n      executeSinglestoreQuery(spark, query = \"CREATE DATABASE test_db_1\")\n      executeSinglestoreQuery(spark, query = \"CREATE TABLE test_db_1.animal (id INT, name TEXT)\")\n\n      val res = executeSinglestoreQueryDB(spark, \"test_db_1\", \"SELECT * FROM animal\")\n      val out =\n        for (row <- res)\n          yield row.getString(1)\n\n      assert(out.toList == List())\n    }\n\n    it(\"executeSinglestoreQuery with numeric columns\") {\n      val params = List(1, 22, 2, null)\n      val res = executeSinglestoreQuery(\n        spark,\n        \"SELECT * FROM numbers WHERE nLong = ? OR nShort = ? OR nFloat = ? OR nDouble = ?\",\n        1,\n        22,\n        2,\n        null)\n      assert(res.length == 2)\n    }\n\n    it(\"executeSinglestoreQuery with byte and date columns\") {\n      val res =\n        executeSinglestoreQuery(\n          spark,\n          \"SELECT * FROM byte_dates WHERE nByte = ? OR nDate = ? OR nTimestamp = ?\",\n          2,\n          \"2015-03-30\",\n          2000)\n      assert(res.length == 2)\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/SQLOverwriteTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.SQLHelper.QueryMethods\nimport com.singlestore.spark.SinglestoreOptions.{OVERWRITE_BEHAVIOR, TRUNCATE}\nimport org.apache.spark.sql.{DataFrame, SaveMode}\nimport org.apache.spark.sql.types.{IntegerType, StringType}\n\nimport scala.List\nimport scala.util.Try\n\nclass SQLOverwriteTest extends IntegrationSuiteBase {\n\n  val dbName    = \"testdb\"\n  val tableName = \"overwrite_table\"\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n    dropTable(tableName)\n  }\n\n  def dropTable(tableName: String): Unit =\n    executeQueryWithLog(s\"drop table if exists $dbName.$tableName\")\n\n  def insertAndAssertEquality(dfBefore: List[(Integer, String)],\n                              dfAfter: List[(Integer, String)],\n                              expected: List[(Integer, String)],\n                              options: Map[String, String],\n                              mode: SaveMode = SaveMode.Overwrite): Unit = {\n    val schema = List((\"id\", IntegerType, true), (\"name\", StringType, true))\n    val dfBeforeOperation = spark.createDF(\n      dfBefore,\n      schema\n    )\n    dfBeforeOperation.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .mode(SaveMode.Overwrite)\n      .option(\"tableKey.primary\", \"id\")\n      .save(s\"${dbName}.${tableName}\")\n\n    val dfAfterOperation = spark.createDF(\n      dfAfter,\n      schema\n    )\n    dfAfterOperation.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .options(options)\n      .mode(mode)\n      .save(s\"${dbName}.${tableName}\")\n\n    val dfExpected = spark.createDF(\n      expected,\n      schema\n    )\n    val actualDF =\n      spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(s\"$dbName.$tableName\")\n    assertLargeDataFrameEquality(actualDF, dfExpected, orderedComparison = false)\n  }\n\n  describe(\"dropAndCreate option\") {\n\n    it(\"success drop and create table with overwriteBehavior option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"dropAndCreate\")\n      )\n    }\n  }\n\n  describe(\"truncate option\") {\n\n    it(\"success truncate with overwriteBehavior option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"truncate\")\n      )\n    }\n\n    it(\"success truncate with truncate option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        Map(SinglestoreOptions.TRUNCATE -> \"true\")\n      )\n    }\n  }\n\n  describe(\"merge option\") {\n\n    it(\"success merge without onDuplicateKeySQL option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((2, \"Charlie\"), (3, \"John\")),\n        List((1, \"Alice\"), (2, \"Charlie\"), (3, \"John\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"merge\")\n      )\n    }\n\n    it(\"success merge with onDuplicateKeySQL option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((2, \"Charlie\"), (3, \"John\")),\n        List((1, \"Alice\"), (2, \"Duplicate\"), (3, \"John\")),\n        Map(SinglestoreOptions.ON_DUPLICATE_KEY_SQL -> \"name = 'Duplicate'\"),\n        SaveMode.Append\n      )\n    }\n\n    it(\"skip merge due to ignore save mode\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((2, \"Charlie\"), (3, \"John\")),\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"John\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"merge\"),\n        SaveMode.Ignore\n      )\n    }\n\n    it(\"still do merge if onDuplicateKeySQL option is defined\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((2, \"Charlie\"), (3, \"John\")),\n        List((1, \"Alice\"), (2, \"Duplicate\"), (3, \"John\")),\n        Map(SinglestoreOptions.ON_DUPLICATE_KEY_SQL -> \"name = 'Duplicate'\"),\n        SaveMode.Ignore\n      )\n    }\n\n    it(\"merge on overwrite with columnstore unique constraints\") {\n      if (version.atLeast(\"7.3.0\")) {\n        spark.executeSinglestoreQuery(query =\n          s\"CREATE TABLE $dbName.$tableName(a INT, b INT, SHARD KEY (a), UNIQUE KEY(a) USING HASH, SORT KEY(a))\")\n        spark.executeSinglestoreQuery(\n          query = s\"INSERT INTO $dbName.$tableName VALUES (1, 1), (2, 2)\")\n\n        val df = spark.createDF(\n          List(\n            (2, 4),\n            (3, 3),\n          ),\n          List((\"a\", IntegerType, false), (\"b\", IntegerType, true))\n        )\n\n        df.write\n          .format(\"singlestore\")\n          .mode(SaveMode.Overwrite)\n          .option(\"overwriteBehavior\", \"merge\")\n          .save(s\"$dbName.$tableName\")\n\n        val resultDf = spark.read.format(\"singlestore\").load(s\"$dbName.$tableName\")\n\n        assertSmallDataFrameEquality(resultDf,\n                                     spark.createDF(\n                                       List(\n                                         (1, 1),\n                                         (2, 4),\n                                         (3, 3),\n                                       ),\n                                       List((\"a\", IntegerType, true), (\"b\", IntegerType, true))\n                                     ),\n                                     orderedComparison = false)\n      }\n    }\n  }\n\n  describe(\"case sensitive success\") {\n\n    it(\"dropandcreate option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"dropandcreate\")\n      )\n    }\n\n    it(\"DROPaNDcREATE option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"DROPaNDcREATE\")\n      )\n    }\n\n    it(\"Truncate option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"Truncate\")\n      )\n    }\n\n    it(\"tRUNCATE option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"tRUNCATE\")\n      )\n    }\n\n    it(\"Merge option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((2, \"Charlie\"), (3, \"John\")),\n        List((1, \"Alice\"), (2, \"Charlie\"), (3, \"John\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"Merge\")\n      )\n    }\n\n    it(\"meRgE option\") {\n      insertAndAssertEquality(\n        List((1, \"Alice\"), (2, \"Bob\")),\n        List((2, \"Charlie\"), (3, \"John\")),\n        List((1, \"Alice\"), (2, \"Charlie\"), (3, \"John\")),\n        Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"meRgE\")\n      )\n    }\n  }\n\n  describe(\"failure during\") {\n\n    it(\"setting wrong overwriteBehavior option\") {\n      val result = Try {\n        insertAndAssertEquality(\n          List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n          List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n          List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n          Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"dropAndCreatea\")\n        )\n      }\n      assert(result.isFailure)\n      result.failed.get match {\n        case ex: IllegalArgumentException\n            if ex.getMessage.equals(\"Illegal argument for `overwriteBehavior` option\") =>\n          succeed\n        case _ => fail()\n      }\n    }\n\n    it(\"set up both truncate and overwriteBehavior options\") {\n      val result = Try {\n        insertAndAssertEquality(\n          List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n          List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n          List((4, \"Charlie\"), (5, \"John\"), (6, \"Mary\")),\n          Map(SinglestoreOptions.OVERWRITE_BEHAVIOR -> \"dropAndCreate\",\n              SinglestoreOptions.TRUNCATE           -> \"true\")\n        )\n      }\n      assert(result.isFailure)\n      result.failed.get match {\n        case ex: IllegalArgumentException\n            if ex.getMessage.equals(\n              s\"can't use both `$TRUNCATE` and `$OVERWRITE_BEHAVIOR` options, please use just `$OVERWRITE_BEHAVIOR` option instead.\") =>\n          succeed\n        case _ => fail()\n      }\n    }\n\n    it(\"duplicate key error if save mode append\") {\n      val result = Try {\n        insertAndAssertEquality(\n          List((1, \"Alice\"), (2, \"Bob\"), (3, \"Eve\")),\n          List((2, \"Charlie\"), (3, \"John\")),\n          List(),\n          Map(),\n          SaveMode.Append\n        )\n      }\n      assert(result.isFailure)\n      /* Error code description:\n        1062 = duplicate key error\n       * */\n      TestHelper.isSQLExceptionWithCode(result.failed.get, List(1062))\n    }\n  }\n\n  describe(\"on duplicate key update\") {\n\n    def createTable(isColumnstore: Boolean): Unit = {\n      val createTableQuery = if (isColumnstore) {\n        s\"CREATE TABLE $dbName.$tableName( srt int, suk int, nuk int, sort key(srt), shard key(suk), key(nuk) using hash, unique key(suk) using hash);\"\n      } else {\n        val rowstore = if (version.atLeast(\"7.5.0\") && isColumnstore) {\n          \"ROWSTORE\"\n        } else {\n          \"\"\n        }\n        s\"CREATE $rowstore TABLE $dbName.$tableName( srt int, suk int, nuk int, sort key(srt), shard key(suk), key(nuk) using hash, key(suk) using hash);\"\n\n      }\n      spark.executeSinglestoreQuery(query = createTableQuery)\n      spark.executeSinglestoreQuery(query = s\"INSERT INTO $dbName.$tableName VALUES (1, 1, 1)\")\n    }\n\n    def assertOnDuplicateKeyUpdateResult[U, T <: Product](rowData: List[U],\n                                                          fields: List[T]): Unit = {\n      assertSmallDataFrameEquality(spark.read.format(\"singlestore\").load(s\"$dbName.$tableName\"),\n                                   spark.createDF(rowData, fields))\n    }\n\n    def onDuplicateKeyUpdate(isColumnstore: Boolean): Unit = {\n      val fields =\n        List((\"srt\", IntegerType, true), (\"suk\", IntegerType, true), (\"nuk\", IntegerType, true))\n      val df = spark.read.format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT).load(s\"$tableName\")\n      df.write\n        .format(\"singlestore\")\n        .option(\"onDuplicateKeySQL\", \"srt = 2\")\n        .option(\"insertBatchSize\", 300)\n        .mode(SaveMode.Append)\n        .save(s\"$dbName.$tableName\")\n\n      if (isColumnstore) {\n        assertOnDuplicateKeyUpdateResult(\n          List(\n            (2, 1, 1),\n          ),\n          fields\n        )\n      } else {\n        assertOnDuplicateKeyUpdateResult(\n          List(\n            (1, 1, 1),\n            (1, 1, 1),\n          ),\n          fields\n        )\n      }\n\n      df.write\n        .format(\"singlestore\")\n        .option(\"onDuplicateKeySQL\", \"nuk = 2\")\n        .option(\"insertBatchSize\", 300)\n        .mode(SaveMode.Append)\n        .save(s\"$dbName.$tableName\")\n\n      if (isColumnstore) {\n        assertOnDuplicateKeyUpdateResult(\n          List(\n            (2, 1, 2),\n          ),\n          fields\n        )\n      } else {\n        assertOnDuplicateKeyUpdateResult(\n          List(\n            (1, 1, 1),\n            (1, 1, 1),\n            (1, 1, 1),\n            (1, 1, 1),\n          ),\n          fields\n        )\n      }\n\n      if (isColumnstore) {\n        val result = Try {\n          df.write\n            .format(\"singlestore\")\n            .option(\"onDuplicateKeySQL\", \"suk = 2\")\n            .option(\"insertBatchSize\", 300)\n            .mode(SaveMode.Append)\n            .save(s\"$dbName.$tableName\")\n        }\n        assert(result.isFailure)\n      }\n    }\n\n    it(\"success update with unique key\") {\n      if (version.atLeast(\"7.3.0\")) {\n        createTable(true)\n        onDuplicateKeyUpdate(true)\n      }\n    }\n\n    it(\"success update without unique key\") {\n      if (version.atLeast(\"7.3.0\")) {\n        createTable(false)\n        onDuplicateKeyUpdate(false)\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/SQLPermissionsTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.util.UUID\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.JdbcHelpers.executeQuery\nimport org.apache.spark.sql.SaveMode\nimport org.apache.spark.sql.types.IntegerType\n\nimport java.sql.DriverManager\nimport scala.util.Try\n\nclass SQLPermissionsTest extends IntegrationSuiteBase {\n\n  val testUserName   = \"sparkuserselect\"\n  val dbName         = \"testdb\"\n  val collectionName = \"temps_test\"\n\n  override def beforeAll(): Unit = {\n    super.beforeAll()\n    val conn =\n      DriverManager.getConnection(s\"jdbc:mysql://$masterHost:$masterPort\", jdbcDefaultProps)\n    executeQuery(conn, s\"CREATE USER '${testUserName}'@'%'\")\n  }\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n    val df = spark.createDF(\n      List(1, 2, 3),\n      List((\"id\", IntegerType, true))\n    )\n    writeTable(s\"${dbName}.${collectionName}\", df)\n  }\n\n  private def setUpUserPermissions(privilege: String): Unit = {\n    /* Revoke all permissions from user */\n    Try(executeQueryWithLog(s\"REVOKE ALL PRIVILEGES ON ${dbName}.* FROM '${testUserName}'@'%'\"))\n    /* Give permissions to user */\n    executeQueryWithLog(s\"GRANT ${privilege} ON ${dbName}.* TO '${testUserName}'@'%'\")\n    /* Set up user to spark */\n    spark.conf.set(\"spark.datasource.singlestore.user\", s\"${testUserName}\")\n  }\n\n  private def doSuccessOperation(operation: () => Unit)(privilege: String): Unit = {\n    it(s\"success with ${privilege} permission\") {\n      setUpUserPermissions(privilege)\n      val result = Try(operation())\n      if (result.isFailure) {\n        result.failed.get.printStackTrace()\n        fail()\n      }\n    }\n  }\n\n  private def doFailOperation(operation: () => Unit)(privilege: String): Unit = {\n    it(s\"fails with ${privilege} permission\") {\n      setUpUserPermissions(privilege)\n      val result = Try(operation())\n      /* Error codes description:\n        1142 = <command> denied to current user\n        1050 = table already exists (error throws when we don't have SELECT permission to check if such table already exists)\n       */\n      assert(TestHelper.isSQLExceptionWithCode(result.failed.get, List(1142, 1050)))\n    }\n  }\n\n  describe(\"read permissions\") {\n    /* List of supported privileges for read operation */\n    val supportedPrivileges = List(\"SELECT\", \"ALL PRIVILEGES\")\n    /* List of unsupported privileges for read operation */\n    val unsupportedPrivileges = List(\"CREATE\", \"DROP\", \"DELETE\", \"INSERT\", \"UPDATE\")\n\n    def operation(): Unit =\n      spark.read\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .load(s\"${dbName}.${collectionName}\")\n\n    unsupportedPrivileges.foreach(doFailOperation(operation))\n    supportedPrivileges.foreach(doSuccessOperation(operation))\n  }\n\n  describe(\"write permissions\") {\n    /* List of supported privileges for write operation */\n    val supportedPrivileges = List(\"INSERT, SELECT\", \"ALL PRIVILEGES\")\n    /* List of unsupported privileges for write operation */\n    val unsupportedPrivileges = List(\"CREATE\", \"DROP\", \"DELETE\", \"SELECT\", \"UPDATE\")\n\n    def operation(): Unit = {\n      val df = spark.createDF(\n        List(4, 5, 6),\n        List((\"id\", IntegerType, true))\n      )\n      df.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .mode(SaveMode.Append)\n        .save(s\"${dbName}.${collectionName}\")\n    }\n\n    unsupportedPrivileges.foreach(doFailOperation(operation))\n    supportedPrivileges.foreach(doSuccessOperation(operation))\n  }\n\n  describe(\"drop permissions\") {\n\n    /* List of supported privileges for drop operation */\n    val supportedPrivileges = List(\"DROP, SELECT, INSERT\", \"ALL PRIVILEGES\")\n    /* List of unsupported privileges for drop operation */\n    val unsupportedPrivileges = List(\"CREATE\", \"INSERT\", \"DELETE\", \"SELECT\", \"UPDATE\")\n\n    implicit def operation(): Unit = {\n      val df = spark.createDF(\n        List(1, 2, 3),\n        List((\"id\", IntegerType, true))\n      )\n      df.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .option(\"truncate\", \"true\")\n        .mode(SaveMode.Overwrite)\n        .save(s\"${dbName}.${collectionName}\")\n    }\n\n    unsupportedPrivileges.foreach(doFailOperation(operation))\n    supportedPrivileges.foreach(doSuccessOperation(operation))\n  }\n\n  describe(\"create permissions\") {\n\n    /* List of supported privileges for create operation */\n    val supportedPrivileges = List(\"CREATE, SELECT, INSERT\", \"ALL PRIVILEGES\")\n    /* List of unsupported privileges for create operation */\n    val unsupportedPrivileges = List(\"DROP\", \"INSERT\", \"DELETE\", \"SELECT\", \"UPDATE\")\n\n    implicit def operation(): Unit = {\n      val df = spark.createDF(\n        List(1, 2, 3),\n        List((\"id\", IntegerType, true))\n      )\n      df.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .mode(SaveMode.Overwrite)\n        .save(s\"${dbName}.${collectionName}_${UUID.randomUUID().toString.split(\"-\")(0)}\")\n    }\n\n    unsupportedPrivileges.foreach(doFailOperation(operation))\n    supportedPrivileges.foreach(doSuccessOperation(operation))\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/SQLPushdownTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.{Relation, SinglestoreVersion}\nimport com.singlestore.spark.SQLHelper._\nimport org.apache.log4j.{Level, LogManager}\nimport org.apache.spark.sql.DataFrame\nimport org.apache.spark.sql.catalyst.plans.logical.LogicalPlan\nimport org.apache.spark.sql.types._\nimport org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach}\nimport scala.util.matching.Regex\n\nclass SQLPushdownTest extends IntegrationSuiteBase with BeforeAndAfterEach with BeforeAndAfterAll {\n\n  override def beforeAll(): Unit = {\n    super.beforeAll()\n    super.beforeEach() // we want to run beforeEach to set up a spark session\n\n    // need to specify explicit schemas - otherwise Spark will infer them\n    // incorrectly from the JSON file\n    val usersSchema = StructType(\n      StructField(\"id\", LongType)\n        :: StructField(\"first_name\", StringType)\n        :: StructField(\"last_name\", StringType)\n        :: StructField(\"email\", StringType)\n        :: StructField(\"owns_house\", BooleanType)\n        :: StructField(\"favorite_color\", StringType, nullable = true)\n        :: StructField(\"age\", IntegerType)\n        :: StructField(\"birthday\", DateType)\n        :: Nil)\n\n    writeTable(\"testdb.users\",\n               spark.read.schema(usersSchema).json(\"src/test/resources/data/users.json\"))\n\n    val moviesSchema = StructType(\n      StructField(\"id\", LongType)\n        :: StructField(\"title\", StringType)\n        :: StructField(\"genre\", StringType)\n        :: StructField(\"critic_review\", StringType, nullable = true)\n        :: StructField(\"critic_rating\", FloatType, nullable = true)\n        :: Nil)\n\n    writeTable(\"testdb.movies\",\n               spark.read.schema(moviesSchema).json(\"src/test/resources/data/movies.json\"))\n\n    val reviewsSchema = StructType(\n      StructField(\"user_id\", LongType)\n        :: StructField(\"movie_id\", LongType)\n        :: StructField(\"rating\", FloatType)\n        :: StructField(\"review\", StringType)\n        :: StructField(\"created\", TimestampType)\n        :: Nil)\n\n    writeTable(\"testdb.reviews\",\n               spark.read.schema(reviewsSchema).json(\"src/test/resources/data/reviews.json\"))\n\n    writeTable(\"testdb.users_sample\",\n               spark.read\n                 .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n                 .load(\"testdb.users\")\n                 .sample(0.5)\n                 .limit(10))\n\n    val movieRatingSchema = StructType(\n      StructField(\"id\", LongType)\n        :: StructField(\"movie_rating\", StringType)\n        :: StructField(\"same_rate_movies\", StringType)\n        :: Nil)\n\n    writeTable(\n      \"testdb.movies_rating\",\n      spark.read.schema(movieRatingSchema).json(\"src/test/resources/data/movies_rating.json\"))\n\n  }\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n\n    spark.sql(\"create database testdb\")\n    spark.sql(\"create database testdb_nopushdown\")\n    spark.sql(\"create database testdb_jdbc\")\n\n    def makeTables(sourceTable: String) = {\n      spark.sql(\n        s\"create table testdb.$sourceTable using singlestore options ('dbtable'='testdb.$sourceTable')\")\n      spark.sql(\n        s\"create table testdb_nopushdown.$sourceTable using memsql options ('dbtable'='testdb.$sourceTable','disablePushdown'='true')\")\n      spark.sql(s\"create table testdb_jdbc.$sourceTable using jdbc options (${jdbcOptionsSQL(\n        s\"testdb.$sourceTable\")})\")\n    }\n\n    makeTables(\"users\")\n    makeTables(\"users_sample\")\n    makeTables(\"movies\")\n    makeTables(\"movies_rating\")\n    makeTables(\"reviews\")\n\n    spark.udf.register(\"stringIdentity\", (s: String) => s)\n  }\n\n  def extractQueriesFromPlan(root: LogicalPlan): Seq[String] = {\n    root\n      .map({\n        case Relation(relation) => relation.sql\n        case _                  => \"\"\n      })\n      .sorted\n  }\n\n  def testCodegenDeterminism(q: String, filterDF: DataFrame => DataFrame): Unit = {\n    val logManager    = LogManager.getLogger(\"com.singlestore.spark.SQLGen$Statement\")\n    var setLogToTrace = false\n\n    if (logManager.isTraceEnabled) {\n      logManager.setLevel(Level.DEBUG)\n      setLogToTrace = true\n    }\n\n    assert(\n      extractQueriesFromPlan(filterDF(spark.sql(q)).queryExecution.optimizedPlan) ==\n        extractQueriesFromPlan(filterDF(spark.sql(q)).queryExecution.optimizedPlan),\n      \"All generated SingleStore queries should be the same\"\n    )\n\n    if (setLogToTrace) {\n      logManager.setLevel(Level.TRACE)\n    }\n  }\n\n  def testQuery(q: String,\n                alreadyOrdered: Boolean = false,\n                expectPartialPushdown: Boolean = false,\n                expectSingleRead: Boolean = false,\n                expectEmpty: Boolean = false,\n                expectSameResult: Boolean = true,\n                expectCodegenDeterminism: Boolean = true,\n                pushdown: Boolean = true,\n                enableParallelRead: String = \"automaticLite\",\n                dataFrameEqualityPrecision: Double = 0.1,\n                filterDF: DataFrame => DataFrame = x => x): Unit = {\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.enableParallelRead\", enableParallelRead)\n\n    spark.sql(\"use testdb_jdbc\")\n    val jdbcDF = filterDF(spark.sql(q))\n\n    // verify that the jdbc DF works first\n    jdbcDF.collect()\n    if (pushdown) { spark.sql(\"use testdb\") } else { spark.sql(\"use testdb_nopushdown\") }\n\n    if (expectCodegenDeterminism) {\n      testCodegenDeterminism(q, filterDF)\n    }\n\n    val singlestoreDF = filterDF(spark.sql(q))\n\n    if (!continuousIntegration) { singlestoreDF.show(4) }\n\n    if (expectEmpty) {\n      assert(singlestoreDF.count == 0, \"result is expected to be empty\")\n    } else {\n      assert(singlestoreDF.count > 0, \"result is expected to not be empty\")\n    }\n\n    if (expectSingleRead) {\n      assert(singlestoreDF.rdd.getNumPartitions == 1,\n             \"query is expected to read from a single partition\")\n    } else {\n      assert(singlestoreDF.rdd.getNumPartitions > 1,\n             \"query is expected to read from multiple partitions\")\n    }\n\n    assert(\n      (singlestoreDF.queryExecution.optimizedPlan match {\n        case SQLGen.Relation(_) => false\n        case _                  => true\n      }) == expectPartialPushdown,\n      s\"the optimized plan does not match expectPartialPushdown=$expectPartialPushdown\"\n    )\n\n    if (expectSameResult) {\n      try {\n        def changeTypes(df: DataFrame): DataFrame = {\n          var newDf = df\n          df.schema\n            .foreach(x =>\n              x.dataType match {\n                // Replace all Floats with Doubles, because JDBC connector converts FLOAT to DoubleType when SingleStore connector converts it to FloatType\n                // Replace all Decimals with Doubles, because assertApproximateDataFrameEquality compare Decimals for strong equality\n                case _: DecimalType | FloatType =>\n                  newDf = newDf.withColumn(x.name, newDf(x.name).cast(DoubleType))\n                // Replace all Shorts with Integers, because JDBC connector converts SMALLINT to IntegerType when SingleStore connector converts it to ShortType\n                case _: ShortType =>\n                  newDf = newDf.withColumn(x.name, newDf(x.name).cast(IntegerType))\n                // Replace all Byte with Integers, because JDBC connector (prior Spark 4.0) converts TINYINT to IntegerType when SingleStore connector converts it to ByteType\n                case _: ByteType =>\n                  newDf = newDf.withColumn(x.name, newDf(x.name).cast(IntegerType))\n                // Replace all CalendarIntervals with Strings, because assertApproximateDataFrameEquality can't sort CalendarIntervals\n                case _: CalendarIntervalType =>\n                  newDf = newDf.withColumn(x.name, newDf(x.name).cast(StringType))\n                case _ =>\n            })\n          newDf\n        }\n        if (alreadyOrdered) {\n          assertApproximateDataFrameEquality(changeTypes(singlestoreDF),\n                                             changeTypes(jdbcDF),\n                                             dataFrameEqualityPrecision)\n        } else {\n          assertApproximateDataFrameEquality(defaultSortDataset(changeTypes(singlestoreDF)),\n                                             defaultSortDataset(changeTypes(jdbcDF)),\n                                             dataFrameEqualityPrecision)\n        }\n      } catch {\n        case e: Throwable =>\n          if (continuousIntegration) { println(singlestoreDF.explain(true)) }\n          throw e\n      }\n    }\n  }\n\n  def testOrderedQuery(q: String, pushdown: Boolean = true): Unit = {\n    // order by in SingleStore requires single read\n    testQuery(q, alreadyOrdered = true, expectSingleRead = true, pushdown = pushdown)\n    afterEach()\n    beforeEach()\n    testQuery(q,\n              alreadyOrdered = true,\n              expectPartialPushdown = true,\n              expectSingleRead = true,\n              pushdown = pushdown,\n              enableParallelRead = \"automatic\")\n    afterEach()\n    beforeEach()\n    testQuery(q,\n              alreadyOrdered = true,\n              expectSingleRead = true,\n              pushdown = pushdown,\n              enableParallelRead = \"disabled\")\n\n  }\n\n  def testSingleReadQuery(q: String,\n                          alreadyOrdered: Boolean = false,\n                          expectPartialPushdown: Boolean = false,\n                          pushdown: Boolean = true): Unit = {\n    testQuery(q,\n              alreadyOrdered = alreadyOrdered,\n              expectPartialPushdown = expectPartialPushdown,\n              expectSingleRead = true,\n              pushdown = pushdown)\n  }\n\n  def testSingleReadForReadFromLeaves(q: String): Unit = {\n    if (canDoParallelReadFromAggregators) {\n      testQuery(q)\n    } else {\n      testSingleReadQuery(q)\n    }\n  }\n\n  def testSingleReadForOldS2(q: String, minVersion: SinglestoreVersion): Unit = {\n    if (version.atLeast(minVersion) && canDoParallelReadFromAggregators) {\n      testQuery(q)\n    } else {\n      testSingleReadQuery(q)\n    }\n  }\n\n  describe(\"Attributes\") {\n    describe(\"successful pushdown\") {\n      it(\"Attribute\") { testQuery(\"select id from users\") }\n      it(\"Alias\") { testQuery(\"select id as user_id from users\") }\n      it(\"Alias with new line\") { testQuery(\"select id as `user_id\\n` from users\") }\n      it(\"Alias with hyphen\") { testQuery(\"select id as `user-id` from users\") }\n      // DatasetComparer fails to sort a DataFrame with weird names, because of it following queries are ran as alreadyOrdered\n      it(\"Alias with dot\") {\n        testOrderedQuery(\"select id as `user.id` from users order by id\")\n      }\n      it(\"Alias with backtick\") {\n        testOrderedQuery(\"select id as `user``id` from users order by id\")\n      }\n    }\n    describe(\"unsuccessful pushdown\") {\n      it(\"alias with udf\") {\n        testQuery(\"select stringIdentity(id) as user_id from users\", expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"Literals\") {\n    describe(\"successful pushdown\") {\n      it(\"string\") {\n        testSingleReadForReadFromLeaves(\"select 'string' from users\")\n      }\n      it(\"null\") {\n        testSingleReadForReadFromLeaves(\"select null from users\")\n      }\n      describe(\"boolean\") {\n        it(\"true\") { testQuery(\"select true from users\") }\n        it(\"false\") { testQuery(\"select false from users\") }\n      }\n\n      it(\"byte\") { testQuery(\"select 100Y from users\") }\n      it(\"short\") { testQuery(\"select 100S from users\") }\n      it(\"integer\") { testQuery(\"select 100 from users\") }\n      it(\"long\") {\n        testSingleReadForReadFromLeaves(\"select 100L from users\")\n      }\n\n      it(\"float\") { testQuery(\"select 1.1 as x from users\") }\n      it(\"double\") { testQuery(\"select 1.1D as x from users\") }\n      it(\"decimal\") { testQuery(\"select 1.1BD as x from users\") }\n\n      it(\"datetime\") { testQuery(\"select date '1997-11-11' as x from users\") }\n    }\n\n    describe(\"unsuccessful pushdown\") {\n      it(\"interval\") {\n        testQuery(\"select interval 1 year 1 month as x from users\", expectPartialPushdown = true)\n      }\n      it(\"binary literal\") {\n        testQuery(\"select X'123456' from users\", expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"Cast\") {\n    describe(\"to boolean\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as boolean) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as boolean) from users\")\n      }\n      it(\"long\") {\n        testQuery(\"select cast(id as boolean) from users\")\n      }\n      it(\"float\") {\n        testQuery(\"select cast(critic_rating as boolean) from movies\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as boolean) from users\")\n      }\n      it(\"timestamp\") {\n        testQuery(\"select cast(created as boolean) from reviews\")\n      }\n    }\n\n    describe(\"to byte\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as byte) from users\")\n      }\n      it(\"int\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min TINYINT value\n        // spark returns module (452->-60)\n        testQuery(\"select cast(age as byte) from users where age > -129 and age < 128\")\n      }\n      it(\"long\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min TINYINT value\n        // spark returns module (452->-60)\n        testQuery(\"select cast(id as byte) from users where id > -129 and id < 128\")\n      }\n      it(\"float\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min TINYINT value\n        // spark returns module (452->-60)\n        // singlestore and spark use different rounding\n        // singlestore round to the closest value\n        // spark round to the smaller one\n        testQuery(\n          \"select cast(critic_rating as byte) from movies where critic_rating > -129 and critic_rating < 128 and critic_rating - floor(critic_rating) < 0.5\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as byte) from users\")\n      }\n      it(\"timestamp\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min TINYINT value\n        // spark returns module (452->-60)\n        testQuery(\n          \"select cast(created as byte) from reviews where cast(created as long) > -129 and cast(created as long) < 128\")\n      }\n    }\n\n    describe(\"to short\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as short) from users\")\n      }\n      it(\"int\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min SMALLINT value\n        // spark returns module (40004->-25532)\n        testQuery(\"select cast(age as short) from users where age > -32769 and age < 32768\")\n      }\n      it(\"long\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min SMALLINT value\n        // spark returns module (40004->-25532)\n        testQuery(\"select cast(id as short) as d from users where id > -32769 and id < 32768\")\n      }\n      it(\"float\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min SMALLINT value\n        // spark returns module (40004->-25532)\n        // singlestore and spark use different rounding\n        // singlestore round to the closest value\n        // spark round to the smaller one\n        testQuery(\n          \"select cast(critic_rating as short) from movies where critic_rating > -32769 and critic_rating < 32768 and critic_rating - floor(critic_rating) < 0.5\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as short) from users\")\n      }\n      it(\"timestamp\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min SMALLINT value\n        // spark returns module (40004->-25532)\n        testQuery(\n          \"select cast(created as short) from reviews where cast(created as long) > -32769 and cast(created as long) < 32768\")\n      }\n    }\n\n    describe(\"to int\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as int) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as int) from users\")\n      }\n      it(\"long\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min INT value\n        // spark returns module (10000000000->1410065408)\n        testQuery(\n          \"select cast(id as int) as d from users where id > -2147483649 and id < 2147483648\")\n      }\n      it(\"float\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min INT value\n        // spark returns module (10000000000->1410065408)\n        // singlestore and spark use different rounding\n        // singlestore round to the closest value\n        // spark round to the smaller one\n        testQuery(\n          \"select cast(critic_rating as int) from movies where critic_rating > -2147483649 and critic_rating < 2147483648 and critic_rating - floor(critic_rating) < 0.5\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as int) from users\")\n      }\n      it(\"timestamp\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min INT value\n        // spark returns module (10000000000->1410065408)\n        testQuery(\n          \"select cast(created as int) from reviews where cast(created as long) > -2147483649 and cast(created as long) < 2147483648\")\n      }\n    }\n\n    describe(\"to long\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as long) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as long) from users\")\n      }\n      it(\"long\") {\n        testQuery(\"select cast(id as long) from users\")\n      }\n      it(\"float\") {\n        // singlestore and spark use different rounding\n        // singlestore round to the closest value\n        // spark round to the smaller one\n        testQuery(\n          \"select cast(critic_rating as long) from movies where critic_rating - floor(critic_rating) < 0.5\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as long) from users\")\n      }\n      it(\"timestamp\") {\n        testQuery(\"select cast(created as long) from reviews\")\n      }\n    }\n\n    describe(\"to float\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as float) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as float) from users\")\n      }\n      it(\"long\") {\n        testQuery(\"select cast(id as float) from users\")\n      }\n      it(\"float\") {\n        testQuery(\"select cast(critic_rating as float) from movies\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as float) from users\")\n      }\n      it(\"timestamp\") {\n        testQuery(\"select cast(created as float) from reviews\")\n      }\n    }\n\n    describe(\"to double\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as double) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as double) from users\")\n      }\n      it(\"long\") {\n        testQuery(\"select cast(id as double) from users\")\n      }\n      it(\"float\") {\n        testQuery(\"select cast(critic_rating as double) from movies\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as double) from users\")\n      }\n      it(\"timestamp\") {\n        testQuery(\"select cast(created as double) from reviews\")\n      }\n    }\n\n    describe(\"to decimal\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as decimal(20, 5)) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as decimal(20, 5)) from users\")\n      }\n      it(\"long\") {\n        // singlestore and spark behaviour differs on the overflow\n        // singlestore returns max/min DECIMAL(x, y) value\n        // spark returns null\n        testQuery(\"select cast(id as decimal(20, 5)) from users where id < 999999999999999.99999\")\n      }\n      it(\"float\") {\n        testQuery(\"select cast(critic_rating as decimal(20, 5)) from movies\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as decimal(20, 5)) from users\")\n      }\n      it(\"timestamp\") {\n        testQuery(\"select cast(created as decimal(20, 5)) from reviews\")\n      }\n    }\n\n    describe(\"to string\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as string) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as string) from users\")\n      }\n      it(\"long\") {\n        testQuery(\"select cast(id as string) from users\")\n      }\n      it(\"float\") {\n        // singlestore converts integers to string with 0 digits after the point\n        // spark adds 1 digit after the point\n        testQuery(\"select cast(cast(critic_rating as string) as float) from movies\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as string) from users\")\n      }\n      it(\"timestamp\") {\n        // singlestore converts timestamps to string with 6 digits after the point\n        // spark adds 0 digit after the point\n        testQuery(\"select cast(cast(created as string) as timestamp) from reviews\")\n      }\n      it(\"string\") {\n        testQuery(\"select cast(first_name as string) from users\")\n      }\n    }\n\n    describe(\"to binary\") {\n      // spark doesn't support casting other types to binary\n      it(\"string\") {\n        testQuery(\"select cast(first_name as binary) from users\")\n      }\n    }\n\n    describe(\"to date\") {\n      it(\"date\") {\n        testQuery(\"select cast(birthday as date) from users\")\n      }\n      it(\"timestamp\") {\n        testQuery(\"select cast(created as date) from reviews\")\n      }\n      it(\"string\") {\n        testQuery(\"select cast(first_name as date) from users\")\n      }\n    }\n\n    describe(\"to timestamp\") {\n      it(\"boolean\") {\n        testQuery(\"select cast(owns_house as timestamp) from users\")\n      }\n      it(\"int\") {\n        testQuery(\"select cast(age as timestamp) from users\")\n      }\n      it(\"long\") {\n        // TIMESTAMP in SingleStore doesn't support values greater then 2147483647999\n        testQuery(\"select cast(id as timestamp) from users where id < 2147483647999\")\n      }\n      it(\"float\") {\n        // singlestore and spark use different rounding\n        // singlestore round to the closest value\n        // spark round to the smaller one\n        testQuery(\n          \"select to_unix_timestamp(cast(critic_rating as timestamp)) as x from movies where critic_rating - floor(critic_rating) < 0.5\")\n      }\n      it(\"date\") {\n        testQuery(\"select cast(birthday as timestamp) from users\")\n      }\n      it(\"timestamp\") {\n        testQuery(\"select cast(created as timestamp) from reviews\")\n      }\n      it(\"string\") {\n        testQuery(\"select cast(first_name as timestamp) from users\")\n      }\n    }\n  }\n\n  describe(\"Variable Expressions\") {\n    describe(\"Coalesce\") {\n      it(\"one non-null value\") { testQuery(\"select coalesce(id) from users\") }\n      it(\"one null value\") {\n        testSingleReadForReadFromLeaves(\"select coalesce(null) from users\")\n      }\n      it(\"a lot of values\") { testQuery(\"select coalesce(null, id, null, id+1) from users\") }\n      it(\"a lot of nulls\") {\n        testSingleReadForReadFromLeaves(\"select coalesce(null, null, null) from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\n          \"select coalesce(stringIdentity(first_name), 'qwerty', 'bob', 'alice') from users\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Least\") {\n      it(\"a lot of ints\") { testQuery(\"select least(id+5, id, 5, id+1) from users\") }\n      it(\"a lot of strings\") {\n        testQuery(\"select least('qwerty', 'bob', first_name, 'alice') from users\")\n      }\n      // SingleStore returns NULL if at least one argument is NULL, when spark skips nulls\n      // it(\"ints with null\") { testQuery(\"select least(null, id, null, id+1) from users\") }\n      it(\"a lot of nulls\") {\n        testSingleReadForReadFromLeaves(\"select least(null, null, null) from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select least('qwerty', 'bob', stringIdentity(first_name), 'alice') from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Greatest\") {\n      it(\"a lot of ints\") { testQuery(\"select greatest(id+5, id, 5, id+1) from users\") }\n      it(\"a lot of strings\") {\n        testQuery(\"select greatest('qwerty', 'bob', first_name, 'alice') from users\")\n      }\n      // SingleStore returns NULL if at least one argument is NULL, when spark skips nulls\n      // it(\"ints with null\") { testQuery(\"select greatest(null, id, null, id+1) from users\") }\n      it(\"a lot of nulls\") {\n        testSingleReadForReadFromLeaves(\"select greatest(null, null, null) from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\n          \"select greatest('qwerty', 'bob', stringIdentity(first_name), 'alice') from users\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Concat\") {\n      it(\"a lot of ints\") { testQuery(\"select concat(id+5, id, 5, id+1) from users\") }\n      it(\"a lot of strings\") {\n        testQuery(\"select concat('qwerty', 'bob', first_name, 'alice') from users\")\n      }\n      it(\"ints with null\") { testQuery(\"select concat(null, id, null, id+1) from users\") }\n      it(\"a lot of nulls\") {\n        testSingleReadForReadFromLeaves(\"select concat(null, null, null) from users\")\n      }\n      it(\"int and string\") { testQuery(\"select concat(id, first_name) from users\") }\n      it(\"same expressions\") {\n        testQuery(\"select  concat(id, id, id) from users\")\n      }\n      it(\"nested same expressions\") {\n        testQuery(\"select * from (select concat(id, id, id) from users)\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select concat('qwerty', 'bob', stringIdentity(first_name), 'alice') from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Elt\") {\n      it(\"a lot of ints\") { testQuery(\"select elt(id+5, id, 5, id+1) from users\") }\n      it(\"a lot of strings\") {\n        testQuery(\"select elt('qwerty', 'bob', first_name, 'alice') from users\")\n      }\n      it(\"ints with null\") { testQuery(\"select elt(null, id, null, id+1) from users\") }\n      it(\"a lot of nulls\") { testQuery(\"select elt(null, null, null) from users\") }\n      it(\"int and string\") { testQuery(\"select elt(id, first_name) from users\") }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select elt('qwerty', 'bob', stringIdentity(first_name), 'alice') from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"Null Expressions\") {\n    describe(\"IfNull\") {\n      it(\"returns the second argument\") {\n        testQuery(\"select IfNull(null, id) as ifn from users\")\n      }\n      it(\"returns the first argument\") {\n        testQuery(\"select IfNull(id, null) as ifn from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select IfNull(stringIdentity(id), id) as ifn from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"NullIf\") {\n      it(\"equal arguments\") {\n        testQuery(\"select id, NullIf(1, 1) as nif from users\")\n      }\n      it(\"non-equal arguments\") {\n        testQuery(\"select NullIf(id, null) as nif from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select IfNull(null, stringIdentity(id)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Nvl\") {\n      it(\"returns the second argument\") {\n        testQuery(\"select Nvl(null, id) as id1 from users\")\n      }\n      it(\"returns the first argument\") {\n        testQuery(\"select Nvl(id, id+1) as id1 from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select Nvl(stringIdentity(id), null) from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"Nvl2\") {\n      it(\"returns the second argument\") {\n        testSingleReadForReadFromLeaves(\"select Nvl2(null, id, 0) as id1 from users\")\n      }\n      it(\"returns the first argument\") {\n        testQuery(\"select Nvl2(id, 10, id+1) as id1 from users\")\n      }\n      it(\"partial pushdown with udf\") { // error\n        testQuery(\"select Nvl2(stringIdentity(id), null, id) as id1 from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"IsNull\") {\n      it(\"not null\") {\n        testQuery(\"select IsNull(favorite_color) from users\")\n      }\n      it(\"null\") {\n        testQuery(\"select IsNull(null) from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select IsNull(stringIdentity(id)) from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"IsNotNull\") {\n      it(\"not null\") {\n        testQuery(\"select IsNotNull(favorite_color) from users\")\n      }\n      it(\"null\") {\n        testQuery(\"select IsNotNull(null) from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select IsNotNull(stringIdentity(id)) from users\", expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"Predicates\") {\n    describe(\"Not\") {\n      it(\"not null\") {\n        testQuery(\"select not(cast(owns_house as boolean)) from users\")\n      }\n      it(\"null\") {\n        testQuery(\"select not(null) from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select not(cast(stringIdentity(id) as boolean)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"If\") {\n      it(\"boolean\") {\n        testQuery(\"select if(cast(owns_house as boolean), first_name, last_name) from users\")\n      }\n      it(\"always true\") {\n        testQuery(\"select if(true, first_name, last_name) from users\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\n          \"select if(cast(stringIdentity(id) as boolean), first_name, last_name) from users\",\n          expectPartialPushdown = true)\n      }\n    }\n    describe(\"case when\") {\n      it(\"simple\") {\n        testQuery(\"select case when id < 10 then 1 else 3 end from users_sample\")\n      }\n\n      it(\"match multiple conditions\") {\n        testQuery(\n          \"select case id when id < 3 then id when id < 6 and id >= 3 then age when id < 9 and id >=6 then last_name else first_name end from users_sample\")\n      }\n\n      it(\"match else condition\") {\n        testQuery(\n          \"select id, case when 1 < 0 then id when 2 < 0 or 3 < 0 then last_name else first_name end from users_sample\")\n      }\n\n      it(\"without else condition, select NULL\") {\n        testQuery(\"select id, case when 1 < 0 then id end from users_sample\")\n      }\n\n      it(\"complicated case: comparing string with a numeric type\") {\n        testQuery(\n          \"select id, case last_name when id < '5'  then 'No_name' when 'Paute' then 'Random_surname' else last_name end from users_sample\",\n          expectSameResult = false\n          // When string is casted to numeric type, singlestore takes the prefix of it\n          // which is numeric (spark returns null if the whole string is not numeric)\n        )\n      }\n      it(\"partial pushdown: invalid condition\") {\n        testQuery(\"select case when IsNull(stringIdentity(id)) then 1 else id end from users\",\n                  expectPartialPushdown = true)\n      }\n\n      it(\"partial pushdown: invalid condition, more complicated query\") {\n        testQuery(\n          \"select case when id < 2 then last_name when IsNull(stringIdentity(id)) then 1 else id end from users\",\n          expectPartialPushdown = true)\n      }\n\n      it(\"partial pushdown: invalid `else` condition\") {\n        testQuery(\"select case when id < 5 then 1 else stringIdentity(id) end from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"arithmetic\") {\n    describe(\"Add\") {\n      it(\"numbers\") { testQuery(\"select user_id + movie_id as x from reviews\") }\n      it(\"floats\") { testQuery(\"select rating + 1.0 as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select stringIdentity(user_id) + movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id + stringIdentity(movie_id) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Subtract\") {\n      it(\"numbers\") { testQuery(\"select user_id - movie_id as x from reviews\") }\n      it(\"floats\") { testQuery(\"select rating - 1.0 as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select stringIdentity(user_id) - movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id - stringIdentity(movie_id) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Multiply\") {\n      it(\"numbers\") { testQuery(\"select user_id * movie_id as x from reviews\") }\n      it(\"floats\") { testQuery(\"select rating * 1.3 as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select stringIdentity(user_id) * movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id * stringIdentity(movie_id) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Divide\") {\n      it(\"numbers\") { testQuery(\"select user_id / movie_id as x from reviews\") }\n      it(\"floats\") { testQuery(\"select rating / 1.3 as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select stringIdentity(user_id) / movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id / stringIdentity(movie_id) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Remainder\") {\n      it(\"numbers\") { testQuery(\"select user_id % movie_id as x from reviews\") }\n      it(\"floats\") { testQuery(\"select rating % 4 as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select stringIdentity(user_id) % movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id % stringIdentity(movie_id) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Pmod\") {\n      it(\"numbers\") { testQuery(\"select pmod(user_id, movie_id) as x from reviews\") }\n      it(\"floats\") { testQuery(\"select pmod(rating, 4) as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select pmod(stringIdentity(user_id), movie_id) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select pmod(user_id, stringIdentity(movie_id)) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"UnaryMinus\") {\n      it(\"numbers\") { testQuery(\"select -id from users\") }\n      it(\"floats\") { testQuery(\"select -critic_rating from movies\") }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select -stringIdentity(id) from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"UnaryPositive\") {\n      it(\"numbers\") { testQuery(\"select +id from users\") }\n      it(\"floats\") { testQuery(\"select +critic_rating from movies\") }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select +stringIdentity(id) from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"Abs\") {\n      it(\"positive numbers\") { testQuery(\"select abs(id) from users\") }\n      it(\"negative numbers\") { testQuery(\"select abs(-id) from users\") }\n      it(\"positive floats\") { testQuery(\"select abs(critic_rating) from movies\") }\n      it(\"negative floats\") { testQuery(\"select abs(-critic_rating) from movies\") }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select abs(stringIdentity(id)) from users\", expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"bitwiseExpressions\") {\n    describe(\"And\") {\n      it(\"succeeds\") { testQuery(\"select user_id & movie_id as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select cast(stringIdentity(user_id) as integer) & movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id & cast(stringIdentity(movie_id) as integer) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Or\") {\n      it(\"numbers\") { testQuery(\"select user_id | movie_id as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select cast(stringIdentity(user_id) as integer) | movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id | cast(stringIdentity(movie_id) as integer) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n    describe(\"Xor\") {\n      it(\"numbers\") { testQuery(\"select user_id ^ movie_id as x from reviews\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select cast(stringIdentity(user_id) as integer) ^ movie_id as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select user_id ^ cast(stringIdentity(movie_id) as integer) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"bitwiseGet\") {\n      it(\"numbers\", ExcludeFromSpark31) {\n        testQuery(\"select bit_get(id, 2) from users_sample\")\n      }\n      it(\"negative left argument\", ExcludeFromSpark31) {\n        try {\n          testQuery(\"select bit_get(id, -2) from users_sample\")\n        } catch {\n          case e: Throwable =>\n            if (e.toString.contains(\"Invalid bit position: -2 is less than zero\") ||\n                e.toString.contains(\"The value of parameter(s) `pos` in `bit_get` is invalid\")) {\n              None\n            } else {\n              throw e\n            }\n        }\n      }\n      it(\"negative right argument\", ExcludeFromSpark31) {\n        testQuery(\"select bit_get(-200, 2), id from users_sample\")\n      }\n      it(\"exceeds upper limit left argument\", ExcludeFromSpark31) {\n        try {\n          testQuery(\"select bit_get(id, 100000) from users_sample\")\n        } catch {\n          case e: Throwable =>\n            if (e.toString\n                  .contains(\"Invalid bit position: 100000 exceeds the bit upper limit\") ||\n                e.toString\n                  .contains(\"The value of parameter(s) `pos` in `bit_get` is invalid\")) {\n              None\n            } else {\n              throw e\n            }\n        }\n      }\n      it(\"big int right argument\", ExcludeFromSpark31) {\n        testQuery(\"select bit_get(1000000000000000, 2), id from users_sample\")\n      }\n      it(\"partial pushdown because of udf in the left argument\", ExcludeFromSpark31) {\n        testQuery(\"select bit_get(cast(stringIdentity(movie_id) as integer), 2) from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\", ExcludeFromSpark31) {\n        testQuery(\"select bit_get(movie_id, cast(stringIdentity(2) as integer)) from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"sanity test disablePushdown\") {\n    def testNoPushdownQuery(q: String, expectSingleRead: Boolean = false): Unit =\n      testQuery(q,\n                expectPartialPushdown = true,\n                pushdown = false,\n                expectSingleRead = expectSingleRead)\n\n    it(\"select all users\") { testNoPushdownQuery(\"select * from users\") }\n    it(\"select all movies\") { testNoPushdownQuery(\"select * from movies\") }\n    it(\"select all reviews\") { testNoPushdownQuery(\"select * from reviews\") }\n    it(\"basic filter\") { testNoPushdownQuery(\"select * from users where id = 1\") }\n    it(\"basic agg\") {\n      testNoPushdownQuery(\"select floor(avg(age)) from users\", expectSingleRead = true)\n    }\n    it(\"numeric order\") {\n      testNoPushdownQuery(\"select * from users order by id asc\", expectSingleRead = true)\n    }\n    it(\"limit with sort\") {\n      testNoPushdownQuery(\"select * from users order by id limit 10\", expectSingleRead = true)\n    }\n    it(\"implicit inner join\") {\n      testNoPushdownQuery(\"select * from users as a, reviews as b where a.id = b.user_id\",\n                          expectSingleRead = true)\n    }\n  }\n\n  describe(\"sanity test the tables\") {\n    it(\"select all users\") { testQuery(\"select * from users\") }\n    it(\"select all users (sampled)\") { testQuery(\"select * from users_sample\") }\n    it(\"select all movies\") { testQuery(\"select * from movies\") }\n    it(\"select all reviews\") { testQuery(\"select * from reviews\") }\n  }\n\n  describe(\"math expressions\") {\n    describe(\"sinh\") {\n      it(\"works with float\") {\n        testQuery(\"select sinh(rating) as sinh from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select sinh(owns_house) as sinh from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select sinh(stringIdentity(rating)) as sinh, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select sinh(null) as sinh from reviews\")\n      }\n    }\n    describe(\"cosh\") {\n      it(\"works with float\") {\n        testQuery(\"select cosh(rating) as cosh from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select cosh(owns_house) as cosh from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select cosh(stringIdentity(rating)) as cosh, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select cosh(null) as cosh from reviews\")\n      }\n    }\n    describe(\"tanh\") {\n      it(\"works with float\") {\n        testQuery(\"select tanh(rating) as tanh from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select tanh(owns_house) as tanh from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select tanh(stringIdentity(rating)) as tanh, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select tanh(null) as tanh from reviews\")\n      }\n    }\n    describe(\"hypot\") {\n      it(\"works with float\") {\n        testQuery(\"select hypot(rating, user_id) as hypot from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select hypot(owns_house, id) as hypot from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select hypot(stringIdentity(rating), user_id) as hypot, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select hypot(null, null) as hypot from reviews\")\n      }\n    }\n    describe(\"rint\") {\n      it(\"works with float\") {\n        testQuery(\"select rint(rating) as rint from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select rint(owns_house) as rint from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select rint(stringIdentity(rating)) as rint, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select rint(null) as rint from reviews\")\n      }\n    }\n\n    describe(\"asinh\") {\n      it(\"works with float\") {\n        testQuery(\"select rating, asinh(rating) as x from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house, asinh(owns_house) as x from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select rating, asinh(stringIdentity(rating)) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select asinh(null) as x from reviews\")\n      }\n    }\n    describe(\"acosh\") {\n      it(\"works with float\") {\n        testQuery(\"select acosh(rating) as acosh from reviews\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select acosh(stringIdentity(rating)) as acosh, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select acosh(null) as acosh from reviews\")\n      }\n    }\n    describe(\"atanh\") {\n      it(\"works with float\") {\n        testQuery(\n          \"select critic_rating, atanh(critic_rating) as x from movies where critic_rating > -1 AND critic_rating < 1\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select rating, atanh(stringIdentity(rating)) as x from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select atanh(null) as x from reviews\")\n      }\n    }\n    describe(\"integralDivide\") {\n      it(\"works with bigint\") {\n        testQuery(\"select user_id, movie_id, user_id div movie_id from reviews\")\n      }\n      it(\"works with null\") {\n        testQuery(\"select null div null as integralDivide from reviews\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(null div null) as integralDivide from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"sqrt\") {\n      it(\"works with float\") {\n        testQuery(\"select sqrt(rating) as sqrt from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select sqrt(owns_house) as sqrt from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select sqrt(stringIdentity(rating)) as sqrt, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select sqrt(null) as sqrt from reviews\")\n      }\n    }\n    describe(\"ceil\") {\n      it(\"works with float\") {\n        testQuery(\"select ceil(rating) as ceil from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select ceil(owns_house) as ceil from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select ceil(stringIdentity(rating)) as ceil, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select ceil(null) as ceil from reviews\")\n      }\n    }\n    describe(\"cos\") {\n      it(\"works with float\") {\n        testQuery(\"select cos(rating) as cos from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select cos(owns_house) as cos from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select cos(stringIdentity(rating)) as cos, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select cos(null) as cos from reviews\")\n      }\n    }\n    describe(\"exp\") {\n      it(\"works with float\") {\n        testQuery(\"select exp(rating) as exp from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select exp(owns_house) as exp from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select exp(stringIdentity(rating)) as exp, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select exp(null) as exp from reviews\")\n      }\n    }\n    describe(\"expm1\") {\n      it(\"works with float\") {\n        testQuery(\"select expm1(rating) as expm1 from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select expm1(owns_house) as expm1 from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select expm1(stringIdentity(rating)) as expm1, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select expm1(null) as expm1 from reviews\")\n      }\n    }\n    describe(\"floor\") {\n      it(\"works with float\") {\n        testQuery(\"select floor(rating) as floor from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select floor(owns_house) as floor from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select floor(stringIdentity(rating)) as floor, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select floor(null) as floor from reviews\")\n      }\n    }\n    describe(\"log\") {\n      it(\"works with float\") {\n        testQuery(\"select log(rating) as log from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select log(owns_house) as log from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select log(stringIdentity(rating)) as log, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select log(null) as log from reviews\")\n      }\n    }\n    describe(\"log2\") {\n      it(\"works with float\") {\n        testQuery(\"select log2(rating) as log2 from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select log2(owns_house) as log2 from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select log2(stringIdentity(rating)) as log2, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select log2(null) as log2 from reviews\")\n      }\n    }\n    describe(\"log10\") {\n      it(\"works with float\") {\n        testQuery(\"select log10(rating) as log10 from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select log10(owns_house) as log10 from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select log10(stringIdentity(rating)) as log10, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select log10(null) as log10 from reviews\")\n      }\n    }\n    describe(\"log1p\") {\n      it(\"works with float\") {\n        testQuery(\"select log1p(rating) as log1p from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select log1p(owns_house) as log1p from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select log1p(stringIdentity(rating)) as log1p, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select log1p(null) as log1p from reviews\")\n      }\n    }\n    describe(\"signum\") {\n      it(\"works with float\") {\n        testQuery(\"select signum(rating) as signum from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select signum(owns_house) as signum from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select signum(stringIdentity(rating)) as signum, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select signum(null) as signum from reviews\")\n      }\n    }\n    describe(\"cot\") {\n      it(\"works with float\") {\n        testQuery(\"select cot(rating) as cot from reviews\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select cot(stringIdentity(rating)) as cot, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select cot(null) as cot from reviews\")\n      }\n    }\n    describe(\"toDegrees\") {\n      it(\"works with float\") {\n        testQuery(\"select degrees(rating) as degrees from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select degrees(owns_house) as degrees from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select degrees(stringIdentity(rating)) as degrees, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select degrees(null) as degrees from reviews\")\n      }\n    }\n    describe(\"toRadians\") {\n      it(\"works with float\") {\n        testQuery(\"select radians(rating) as radians from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select radians(owns_house) as radians from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select radians(stringIdentity(rating)) as radians, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select radians(null) as radians from reviews\")\n      }\n    }\n    describe(\"bin\") {\n      it(\"works with float\") {\n        testQuery(\"select bin(user_id) as bin from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select bin(owns_house) as bin from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select bin(stringIdentity(rating)) as bin, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testSingleReadForReadFromLeaves(\"select bin(null) as bin from reviews\")\n      }\n    }\n    describe(\"hex\") {\n      it(\"works with float\") {\n        testQuery(\"select hex(user_id) as hex from reviews\")\n      }\n      it(\"works with tinyint\") {\n        testQuery(\"select hex(owns_house) as hex from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select hex(stringIdentity(rating)) as hex, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testSingleReadForReadFromLeaves(\"select hex(null) as hex from reviews\")\n      }\n    }\n    describe(\"unhex\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select unhex(owns_house) as unhex from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select unhex(stringIdentity(rating)) as unhex, stringIdentity(review) as review from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"works with null\") {\n        testQuery(\"select unhex(null) as unhex from reviews\")\n      }\n    }\n\n    it(\"sinh\") {\n      testQuery(\"select sinh(rating) as sinh from reviews\")\n    }\n    it(\"cosh\") {\n      testQuery(\"select cosh(rating) as cosh from reviews\")\n    }\n    it(\"tanh\") {\n      testQuery(\"select tanh(rating) as tanh from reviews\")\n    }\n    it(\"hypot\") {\n      testQuery(\"select hypot(rating, user_id) as hypot from reviews\")\n    }\n    it(\"rint\") {\n      testQuery(\"select rint(rating) as rint from reviews\")\n    }\n\n    describe(\"Atan2\") {\n      // atan(-e,-1) = -pi\n      // atan(e,-1) = pi\n      // where e is a very small value\n      // we are filtering this cases, because the result can differ\n      // for singlestore and spark, because of precision loss\n      it(\"works\") {\n        testQuery(\"\"\"select \n            | atan2(critic_rating, id) as a1, \n            | atan2(critic_rating, -id) as a2,\n            | atan2(-critic_rating, id) as a3,\n            | atan2(-critic_rating, -id) as a4,\n            | critic_rating, id\n            | from movies where abs(critic_rating) > 0.01 or critic_rating is null\"\"\".stripMargin)\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select atan2(stringIdentity(critic_rating), id) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select atan2(critic_rating, stringIdentity(id)) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Pow\") {\n      it(\"works\") {\n        testQuery(\"select log(pow(id, critic_rating)) as x from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select log(pow(stringIdentity(id), critic_rating)) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select log(pow(id, stringIdentity(critic_rating))) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Logarithm\") {\n      it(\"works\") {\n        // spark returns +/-Infinity for log(1, x)\n        // singlestore returns NULL for it\n        testQuery(\"\"\"select \n            | log(critic_rating, id) as l1, \n            | log(critic_rating, -id) as l2,\n            | log(-critic_rating, id) as l3,\n            | log(-critic_rating, -id) as l4,\n            | log(id, critic_rating) as l5, \n            | log(id, -critic_rating) as l6,\n            | log(-id, critic_rating) as l7,\n            | log(-id, -critic_rating) as l8,\n            | critic_rating, id\n            | from movies where id != 1 and critic_rating != 1\"\"\".stripMargin)\n      }\n      it(\"works with 1 argument\") {\n        testQuery(\"select log(critic_rating) as x from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select log(stringIdentity(id), critic_rating) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select log(id, stringIdentity(critic_rating)) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Round\") {\n      // singlestore can round x.5 differently\n      it(\"works with one argument\") {\n        testQuery(\"\"\"select \n            |round(critic_rating), \n            |round(-critic_rating),\n            |critic_rating from movies \n            |where critic_rating - floor(critic_rating) != 0.5\"\"\".stripMargin)\n      }\n      it(\"works with two arguments\") {\n        testQuery(\"\"\"select \n            |round(critic_rating/10.0, 1) as x1, \n            |round(-critic_rating/100.0, 2) as x2,\n            |critic_rating from movies \n            |where critic_rating - floor(critic_rating) != 0.5\"\"\".stripMargin)\n      }\n      it(\"works with negative scale\") {\n        testQuery(\"select round(critic_rating, -2) as x from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select round(stringIdentity(critic_rating), 2) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n      // right argument must be foldable\n      // because of it, we can't use udf there\n    }\n\n    describe(\"WidthBucket\") {\n      it(\"works with int, simple case\") {\n        testQuery(\"select id, width_bucket(id, 10, 90, 10) as x from movies\")\n      }\n      it(\"works with int, negative value\") {\n        testQuery(\"select width_bucket(-id, -90, -10, 10) as x from movies\")\n      }\n      it(\"many small buckets\") {\n        testQuery(\"select id, width_bucket(id, 10, 90, 1000000000) as x from movies\")\n      }\n      it(\"works with int, max < min\") {\n        testQuery(\"select id, width_bucket(id, 90, 10, 10) as x from movies\")\n      }\n      it(\"num_buckets is float\") {\n        testQuery(\"select id, width_bucket(id, 90, 10, 6.6) as x from movies\")\n      }\n      it(\"num_buckets is non-constant  float\") {\n        testQuery(\n          \"select id, width_bucket(id, 10, 90, id/10 + 1) as x from movies where id/10 - floor(id/10) < 0.5\")\n      }\n      it(\"works with float, simple case\") {\n        testQuery(\n          \"select id, width_bucket(critic_rating, 10 + critic_rating, 90, 10) as x from movies\")\n      }\n      it(\"works with string\") {\n        testQuery(\"select id, width_bucket(-critic_rating, '10.2', '0.4', '10') as x from movies\")\n      }\n      it(\"all args are not const\") {\n        testQuery(\"select id, width_bucket(critic_rating, id-10, id, id + 200) as x from movies\")\n      }\n      it(\"works with int, max = min\") {\n        testQuery(\"select id, width_bucket(id, 90, 90, 10) as x from movies\")\n      }\n      it(\"num_buckets is negative\") {\n        testQuery(\"select id, width_bucket(id, 90, 10, -1) as x from movies\")\n      }\n      it(\"num_buckets = 0\") {\n        testQuery(\"select id, width_bucket(id, 90, 10, 0) as x from movies\")\n      }\n      it(\"udf in the first arg\") {\n        testQuery(\n          \"select id, width_bucket(stringIdentity(critic_rating), 10, 100, 200) as x from movies\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the second arg\") {\n        testQuery(\n          \"select id, width_bucket(id, stringIdentity(critic_rating), 100, 200) as x from movies\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the third arg\") {\n        testQuery(\n          \"select id, width_bucket(id, 10, stringIdentity(critic_rating), 200) as x from movies\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the fourth arg\") {\n        testQuery(\n          \"select id, width_bucket(id, 100, 200, stringIdentity(critic_rating)) as x from movies\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Hypot\") {\n      it(\"works\") {\n        testQuery(\"\"\"select \n            | Hypot(critic_rating, id) as h1, \n            | Hypot(critic_rating, -id) as h2,\n            | Hypot(-critic_rating, id) as h3,\n            | Hypot(-critic_rating, -id) as h4,\n            | Hypot(id, critic_rating) as h5, \n            | Hypot(id, -critic_rating) as h6,\n            | Hypot(-id, critic_rating) as h7,\n            | Hypot(-id, -critic_rating) as h8,\n            | critic_rating, id\n            | from movies\"\"\".stripMargin)\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select Hypot(stringIdentity(critic_rating), id) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select Hypot(critic_rating, stringIdentity(id)) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    it(\"EulerNumber\") {\n      testQuery(\"select E() from movies\")\n    }\n\n    it(\"Pi\") {\n      testQuery(\"select PI() from movies\")\n    }\n\n    describe(\"Conv\") {\n      // singlestore and spark behaviour differs when num contains non alphanumeric characters\n      val bases = Seq(2, 5, 23, 36)\n      it(\"works\") {\n        for (fromBase <- bases; toBase <- bases) {\n          log.debug(s\"testing conv $fromBase -> $toBase\")\n          testQuery(s\"\"\"select\n               |first_name,\n               |conv(first_name, $fromBase, $toBase)\n               |from users\n               |where first_name rlike '^[a-zA-Z0-9]*$$'\"\"\".stripMargin)\n        }\n      }\n      it(\"works with numeric\") {\n        testQuery(\"select conv(id, 10, 2) from users\")\n      }\n      it(\"partial pushdown when fromBase out of range [2, 36]\") {\n        testQuery(\"select conv(first_name, 1, 20) from users\", expectPartialPushdown = true)\n      }\n      it(\"partial pushdown when toBase out of range [2, 36]\") {\n        testQuery(\"select conv(first_name, 20, 1) from users\", expectPartialPushdown = true)\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select conv(stringIdentity(first_name), 20, 15) from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\"select conv(first_name, stringIdentity(20), 15) from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the third argument\") {\n        testQuery(\"select conv(first_name, 20, stringIdentity(15)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"ShiftLeft\") {\n      it(\"works\") {\n        testQuery(\"select ShiftLeft(id, floor(critic_rating)) as x from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select ShiftLeft(stringIdentity(id), floor(critic_rating)) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select ShiftLeft(id, stringIdentity(floor(critic_rating))) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"ShiftRight\") {\n      it(\"works\") {\n        testQuery(\"select ShiftRight(id, floor(critic_rating)) as x from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select ShiftRight(stringIdentity(id), floor(critic_rating)) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select ShiftRight(id, stringIdentity(floor(critic_rating))) as x from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"ShiftRightUnsigned\") {\n      it(\"works\") {\n        testQuery(\"select ShiftRightUnsigned(id, floor(critic_rating)) as x from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\n          \"select ShiftRightUnsigned(stringIdentity(id), floor(critic_rating)) as x from movies\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\n          \"select ShiftRightUnsigned(id, stringIdentity(floor(critic_rating))) as x from movies\",\n          expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"bit operations\") {\n    it(\"bit_count\") { testQuery(\"SELECT bit_count(user_id) AS bit_count FROM reviews\") }\n    def bitOperationTest(sql: String): Unit = {\n      val bitOperationsMinVersion = SinglestoreVersion(7, 0, 1)\n      val resultSet               = spark.executeSinglestoreQuery(\"select @@memsql_version\")\n      val version                 = SinglestoreVersion(resultSet.next().getString(0))\n      if (version.atLeast(bitOperationsMinVersion))\n        testSingleReadForOldS2(sql, SinglestoreVersion(7, 6, 0))\n    }\n    it(\"bit_and\") {\n      bitOperationTest(\"SELECT bit_and(user_id) AS bit_and FROM reviews\")\n    }\n    it(\"bit_and filter\") {\n      bitOperationTest(\"SELECT bit_and(user_id) filter (where user_id % 2 = 0) FROM reviews\")\n    }\n    it(\"bit_or\") { bitOperationTest(\"SELECT bit_or(age) AS bit_or FROM users\") }\n    it(\"bit_or filter\") {\n      bitOperationTest(\"SELECT bit_or(age) filter (where age % 2 = 0) FROM users\")\n    }\n    it(\"bit_xor\") {\n      bitOperationTest(\"SELECT bit_xor(user_id) AS bit_xor FROM reviews\")\n    }\n    it(\"bit_xor filter\") {\n      bitOperationTest(\"SELECT bit_xor(user_id) filter (where user_id % 2 = 0) FROM reviews\")\n    }\n  }\n\n  describe(\"datatypes\") {\n    // due to a bug in our dataframe comparison library we need to alias the column 4.9 to x...\n    // this is because when the library asks spark for a column called \"4.9\", spark thinks the\n    // library wants the table 4 and column 9.\n    it(\"float literal\") { testQuery(\"select 4.9 as x from movies\") }\n\n    it(\"negative float literal\") { testQuery(\"select -24.345 as x from movies\") }\n    it(\"negative int literal\") { testQuery(\"select -1 from users\") }\n\n    it(\"int\") { testQuery(\"select id from users\") }\n    it(\"smallint\") { testQuery(\"select age from users\") }\n    it(\"date\") { testQuery(\"select birthday from users\") }\n    it(\"datetime\") { testQuery(\"select created from reviews\") }\n    it(\"bool\") { testQuery(\"select owns_house from users\") }\n    it(\"float\") { testQuery(\"select critic_rating from movies\") }\n    it(\"text\") { testQuery(\"select first_name from users\") }\n\n    it(\"typeof\") {\n      testSingleReadForReadFromLeaves(\n        \"SELECT typeof(user_id), typeof(created), typeof(review) FROM reviews\")\n    }\n  }\n\n  describe(\"filter\") {\n    it(\"numeric equality\") { testQuery(\"select * from users where id = 1\") }\n    it(\"numeric inequality\") { testQuery(\"select * from users where id != 1\") }\n    it(\"numeric comparison >\") { testQuery(\"select * from users where id > 500\") }\n    it(\"numeric comparison > <\") { testQuery(\"select * from users where id > 500 and id < 550\") }\n    it(\"string equality\") { testQuery(\"select * from users where first_name = 'Evan'\") }\n  }\n\n  describe(\"Aggregate Expressions\") {\n    describe(\"some aggregation functions\") {\n      val functions = {\n        Seq(\"skewness\", \"kurtosis\", \"var_pop\", \"var_samp\", \"stddev_samp\", \"stddev_pop\", \"avg\")\n      }\n      it(\"ints\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f (user_id) as x from reviews\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"floats\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f (rating) as x from reviews\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"floats with nulls\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f (critic_rating) as x from movies\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"partial pushdown with udf\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadQuery(s\"select $f (stringIdentity(user_id)) as x from reviews\",\n                              expectPartialPushdown = true)\n        }\n      }\n      it(\"filter\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f(age) filter (where age % 2 = 0) from users\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"filter for equal range population(std = 0)\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f(age) filter (where age = 60) from users\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n    }\n\n    describe(\"min/max functions\") {\n      val functions = {\n        Seq(\"min\", \"max\")\n      }\n      it(\"ints\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f (user_id) as x from reviews\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"floats\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f (rating) as x from reviews\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"strings\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f (first_name) as x from users\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"floats with nulls\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f (critic_rating) as x from movies\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n      it(\"partial pushdown with udf\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadQuery(s\"select $f (stringIdentity(user_id)) as x from reviews\",\n                              expectPartialPushdown = true)\n        }\n      }\n      it(\"filter\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testSingleReadForOldS2(s\"select $f(age) filter (where age % 2 = 0) from users\",\n                                 SinglestoreVersion(7, 6, 0))\n        }\n      }\n    }\n\n    describe(\"Sum\") {\n      // We cast the output, because SingleStore SUM returns DECIMAL(41, 0)\n      // which is not supported by spark (spark maximum decimal precision is 38)\n      it(\"ints\") {\n        testSingleReadForOldS2(\"select cast(sum(age) as decimal(20, 0)) as x from users\",\n                               SinglestoreVersion(7, 6, 0))\n      }\n      it(\"floats\") {\n        testSingleReadForOldS2(\"select sum(rating) as x from reviews\", SinglestoreVersion(7, 6, 0))\n      }\n      it(\"floats with nulls\") {\n        testSingleReadForOldS2(\"select sum(critic_rating) as x from movies\",\n                               SinglestoreVersion(7, 6, 0))\n      }\n      it(\"partial pushdown with udf\") {\n        testSingleReadQuery(\"select sum(stringIdentity(user_id)) as x from reviews\",\n                            expectPartialPushdown = true)\n      }\n    }\n    describe(\"First\") {\n      it(\"succeeds\") {\n        testSingleReadForReadFromLeaves(\"select first(first_name) from users group by id\")\n      }\n      it(\"partial pushdown with udf\") {\n        testSingleReadQuery(\"select first(stringIdentity(first_name)) from users group by id\",\n                            expectPartialPushdown = true)\n      }\n      it(\"filter\") {\n        testSingleReadForReadFromLeaves(\n          \"select first(first_name) filter (where age % 2 = 0) from users group by id\")\n      }\n    }\n    describe(\"Last\") {\n      it(\"succeeds\") {\n        testSingleReadForReadFromLeaves(\"select last(first_name) from users group by id\")\n      }\n      it(\"partial pushdown with udf\") {\n        testSingleReadQuery(\"select last(stringIdentity(first_name)) from users group by id\",\n                            expectPartialPushdown = true)\n      }\n      it(\"filter\") {\n        testSingleReadForReadFromLeaves(\n          \"select last(first_name) filter (where age % 2 = 0) from users group by id\")\n      }\n    }\n    describe(\"Count\") {\n      it(\"all\") {\n        testSingleReadForOldS2(\"select count(*) from users\", SinglestoreVersion(7, 6, 0))\n      }\n      it(\"distinct\") {\n        testSingleReadForOldS2(\"select count(distinct first_name) from users\",\n                               SinglestoreVersion(7, 6, 0))\n      }\n      it(\"partial pushdown with udf (all)\") {\n        testSingleReadQuery(\"select count(stringIdentity(first_name)) from users group by id\",\n                            expectPartialPushdown = true)\n      }\n      it(\"partial pushdown with udf (distinct)\") {\n        testSingleReadQuery(\n          \"select count(distinct stringIdentity(first_name)) from users group by id\",\n          expectPartialPushdown = true)\n      }\n      it(\"filter\") {\n        testSingleReadForOldS2(\"select count(*) filter (where age % 2 = 0) from users\",\n                               SinglestoreVersion(7, 6, 0))\n      }\n      it(\"partial pushdown with udf in filter\") {\n        spark.udf.register(\"myUDF\", (x: Int) => x % 3 == 1)\n        testSingleReadQuery(\"SELECT count_if(age % 2 = 0) filter (where myUDF(age)) FROM users\",\n                            expectPartialPushdown = true)\n      }\n      it(\"count_if\") {\n        testSingleReadForOldS2(\"SELECT count_if(age % 2 = 0) as count FROM users\",\n                               SinglestoreVersion(7, 6, 0))\n      }\n      it(\"count_if filter\") {\n        testSingleReadForOldS2(\"SELECT count_if(age % 2 = 0) filter (where age % 2 = 0) FROM users\",\n                               SinglestoreVersion(7, 6, 0))\n      }\n    }\n    it(\"top 3 email domains\") {\n      testOrderedQuery(\n        \"\"\"\n          |   select domain, count(*) from (\n          |     select substring(email, locate('@', email) + 1) as domain\n          |     from users\n          |   )\n          |   group by 1\n          |   order by 2 desc, 1 asc\n          |   limit 3\n          |\"\"\".stripMargin\n      )\n    }\n  }\n\n  describe(\"window functions\") {\n    it(\"rank order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select out as a from (select rank() over (order by first_name) as out from users)\")\n    }\n    it(\"rank partition order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select rank() over (partition by first_name order by first_name) as out from users\")\n    }\n    it(\"row_number order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select row_number() over (order by first_name) as out from users\")\n    }\n    it(\"dense_rank order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select dense_rank() over (order by first_name) as out from users\")\n    }\n    it(\"lag order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select first_name, lag(first_name) over (order by first_name) as out from users\")\n    }\n    it(\"lead order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select first_name, lead(first_name) over (order by first_name) as out from users\")\n    }\n    it(\"ntile(3) order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select first_name, ntile(3) over (order by first_name) as out from users\")\n    }\n    it(\"percent_rank order by\") {\n      testSingleReadForReadFromLeaves(\n        \"select first_name, percent_rank() over (order by first_name) as out from users\")\n    }\n  }\n\n  describe(\"sort/limit\") {\n    it(\"numeric order\") {\n      testOrderedQuery(\"select * from users order by id asc\")\n    }\n    it(\"text order\") {\n      testOrderedQuery(\"select * from users order by first_name desc, last_name asc, id asc\")\n    }\n    it(\"text order expression\") {\n      testOrderedQuery(\"select * from users order by `email` like '%@gmail%', id asc\")\n    }\n\n    it(\"text order case\") {\n      testOrderedQuery(\n        \"select * from users where first_name in ('Abbey', 'a') order by first_name desc, id asc\")\n    }\n\n    it(\"simple limit\") {\n      testSingleReadForReadFromLeaves(\"select 'a' from users limit 10\")\n    }\n    it(\"limit with sort\") {\n      testOrderedQuery(\"select * from users order by id limit 10\")\n    }\n    it(\"limit with sort on inside\") {\n      testOrderedQuery(\"select * from (select * from users order by id) limit 10\")\n    }\n    it(\"limit with sort on outside\") {\n      testOrderedQuery(\n        \"select * from (select id from (select id, email from users order by id limit 10) limit 100) order by id\"\n      )\n    }\n  }\n\n  describe(\"hashes\") {\n    describe(\"sha1\") {\n      it(\"works with text\") { testQuery(\"select sha1(first_name) from users\") }\n      it(\"works with null\") {\n        testSingleReadForReadFromLeaves(\"select sha1(null) from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select sha1(stringIdentity(first_name)) as sha1, stringIdentity(first_name) as first_name from users\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"sha2\") {\n      it(\"0 bit length\") { testQuery(\"select sha2(first_name, 0) from users\") }\n      it(\"256 bit length\") { testQuery(\"select sha2(first_name, 256) from users\") }\n      it(\"384 bit length\") { testQuery(\"select sha2(first_name, 384) from users\") }\n      it(\"512 bit length\") { testQuery(\"select sha2(first_name, 512) from users\") }\n      it(\"works with null\") {\n        testSingleReadForReadFromLeaves(\"select sha2(null, 256) from users\")\n      }\n      it(\"224 bit length partial pushdown\") {\n        testQuery(\"select sha2(first_name, 224) from users\", expectPartialPushdown = true)\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select sha2(stringIdentity(first_name), 256) as sha2, stringIdentity(first_name) as first_name from users\",\n          expectPartialPushdown = true)\n      }\n      it(\"short literal\") {\n        testQuery(s\"select sha2(first_name, 256S) from users\")\n      }\n      it(\"long literal\") {\n        testQuery(s\"select sha2(first_name, 256L) from users\")\n      }\n    }\n\n    describe(\"md5\") {\n      it(\"works with text\") { testQuery(\"select md5(first_name) from users\") }\n      it(\"works with null\") {\n        testSingleReadForReadFromLeaves(\"select md5(null) from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select md5(stringIdentity(first_name)) as md5, stringIdentity(first_name) as first_name from users\",\n          expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"joins\") {\n    describe(\"successful pushdown\") {\n      it(\"implicit inner join\") {\n        testSingleReadForReadFromLeaves(\n          \"select * from users as a, reviews where a.id = reviews.user_id\")\n      }\n      it(\"explicit inner join\") {\n        testSingleReadForReadFromLeaves(\n          \"select * from users inner join reviews on users.id = reviews.user_id\")\n      }\n      it(\"cross join\") {\n        testSingleReadForReadFromLeaves(\n          \"select * from users cross join reviews on users.id = reviews.user_id\")\n      }\n      it(\"cross join with sort and limit\") {\n        testSingleReadForReadFromLeaves(\n          \"\"\"select * from \n                      |  (select * from users order by id limit 10) as users cross join \n                      |  (select * from reviews order by user_id limit 10) as reviews \n                      |on users.id = reviews.user_id\"\"\".stripMargin)\n      }\n      it(\"left outer join\") {\n        testSingleReadForReadFromLeaves(\n          \"select * from users left outer join reviews on users.id = reviews.user_id\")\n      }\n      it(\"right outer join\") {\n        testSingleReadForReadFromLeaves(\n          \"select * from users right outer join reviews on users.id = reviews.user_id\")\n      }\n      it(\"full outer join\") {\n        testSingleReadForReadFromLeaves(\n          \"select * from users full outer join reviews on users.id = reviews.user_id\")\n      }\n      it(\"natural join\") {\n        testSingleReadForReadFromLeaves(\n          \"select users.id, rating from users natural join (select user_id as id, rating from reviews)\")\n      }\n      it(\"complex join\") {\n        testSingleReadForReadFromLeaves(\n          \"\"\"\n                      |  select users.id, round(avg(rating), 2) as rating, count(*) as num_reviews\n                      |  from users inner join reviews on users.id = reviews.user_id\n                      | group by users.id\n                      |\"\"\".stripMargin)\n      }\n      it(\"inner join without condition\") {\n        testOrderedQuery(\n          \"select * from users inner join reviews order by concat(users.id, ' ', reviews.user_id, ' ', reviews.movie_id) limit 10\")\n      }\n\n      it(\"cross join without condition\") {\n        testOrderedQuery(\n          \"select * from users cross join reviews order by concat(users.id, ' ', reviews.user_id, ' ', reviews.movie_id) limit 10\")\n      }\n    }\n\n    describe(\"unsuccessful pushdown\") {\n      describe(\"udf in the left relation\") {\n        it(\"explicit inner join\") {\n          testSingleReadQuery(\n            \"select * from (select rating, stringIdentity(user_id) as user_id from reviews) inner join users on users.id = user_id\",\n            expectPartialPushdown = true)\n        }\n        it(\"cross join\") {\n          testSingleReadQuery(\n            \"select * from (select rating, stringIdentity(user_id) as user_id from reviews) cross join users on users.id = user_id\",\n            expectPartialPushdown = true)\n        }\n        it(\"left outer join\") {\n          testSingleReadQuery(\n            \"select * from (select rating, stringIdentity(user_id) as user_id from reviews) left outer join users on users.id = user_id\",\n            expectPartialPushdown = true\n          )\n        }\n        it(\"right outer join\") {\n          testSingleReadQuery(\n            \"select * from (select rating, stringIdentity(user_id) as user_id from reviews) right outer join users on users.id = user_id\",\n            expectPartialPushdown = true\n          )\n        }\n        it(\"full outer join\") {\n          testSingleReadQuery(\n            \"select * from (select rating, stringIdentity(user_id) as user_id from reviews) full outer join users on users.id = user_id\",\n            expectPartialPushdown = true\n          )\n        }\n      }\n\n      describe(\"udf in the right relation\") {\n        it(\"explicit inner join\") {\n          testSingleReadQuery(\n            \"select * from users inner join (select rating, stringIdentity(user_id) as user_id from reviews) on users.id = user_id\",\n            expectPartialPushdown = true)\n        }\n        it(\"cross join\") {\n          testSingleReadQuery(\n            \"select * from users cross join (select rating, stringIdentity(user_id) as user_id from reviews) on users.id = user_id\",\n            expectPartialPushdown = true)\n        }\n        it(\"left outer join\") {\n          testSingleReadQuery(\n            \"select * from users left outer join (select rating, stringIdentity(user_id) as user_id from reviews) on users.id = user_id\",\n            expectPartialPushdown = true\n          )\n        }\n        it(\"right outer join\") {\n          testSingleReadQuery(\n            \"select * from users right outer join (select rating, stringIdentity(user_id) as user_id from reviews) on users.id = user_id\",\n            expectPartialPushdown = true\n          )\n        }\n        it(\"full outer join\") {\n          testSingleReadQuery(\n            \"select * from users full outer join (select rating, stringIdentity(user_id) as user_id from reviews) on users.id = user_id\",\n            expectPartialPushdown = true\n          )\n        }\n      }\n\n      describe(\"udf in the condition\") {\n        it(\"explicit inner join\") {\n          testSingleReadQuery(\n            \"select * from users inner join reviews on stringIdentity(users.id) = stringIdentity(reviews.user_id)\",\n            expectPartialPushdown = true)\n        }\n        it(\"cross join\") {\n          testSingleReadQuery(\n            \"select * from users cross join reviews on stringIdentity(users.id) = stringIdentity(reviews.user_id)\",\n            expectPartialPushdown = true)\n        }\n        it(\"left outer join\") {\n          testSingleReadQuery(\n            \"select * from users left outer join reviews on stringIdentity(users.id) = stringIdentity(reviews.user_id)\",\n            expectPartialPushdown = true)\n        }\n        it(\"right outer join\") {\n          testSingleReadQuery(\n            \"select * from users right outer join reviews on stringIdentity(users.id) = stringIdentity(reviews.user_id)\",\n            expectPartialPushdown = true)\n        }\n        it(\"full outer join\") {\n          testSingleReadQuery(\n            \"select * from users full outer join reviews on stringIdentity(users.id) = stringIdentity(reviews.user_id)\",\n            expectPartialPushdown = true)\n        }\n      }\n\n      describe(\"outer joins with empty condition\") {\n        it(\"left outer join\") {\n          testQuery(\n            \"select * from users left outer join (select rating from reviews order by rating limit 10)\",\n            expectPartialPushdown = true)\n        }\n        it(\"right outer join\") {\n          testSingleReadQuery(\n            \"select * from users right outer join (select rating from reviews order by rating limit 10) order by age\",\n            expectPartialPushdown = true)\n        }\n        it(\"full outer join\") {\n          testQuery(\n            \"select * from users full outer join (select rating from reviews order by rating limit 10)\",\n            expectPartialPushdown = true)\n        }\n      }\n\n      describe(\"different dml jdbc options\") {\n        def testPushdown(joinType: String): Unit = {\n          val df1 =\n            spark.read\n              .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n              .options(Map(\"dmlEndpoint\" -> \"host1:1020,host2:1010\"))\n              .load(\"testdb.users\")\n          val df2 =\n            spark.read\n              .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n              .options(Map(\"dmlEndpoint\" -> \"host3:1020,host2:1010\"))\n              .load(\"testdb.reviews\")\n\n          val joinedDf = df1.join(df2, df1(\"id\") === df2(\"user_id\"), joinType)\n          log.debug(joinedDf.queryExecution.optimizedPlan.toString())\n          assert(\n            joinedDf.queryExecution.optimizedPlan match {\n              case SQLGen.Relation(_) => false\n              case _                  => true\n            },\n            \"Join of the relations with different jdbc connection options should not be pushed down\"\n          )\n        }\n\n        it(\"explicit inner join\") {\n          testPushdown(\"inner\")\n        }\n        it(\"cross join\") {\n          testPushdown(\"cross\")\n        }\n        it(\"left outer join\") {\n          testPushdown(\"leftouter\")\n        }\n        it(\"right outer join\") {\n          testPushdown(\"rightouter\")\n        }\n        it(\"full outer join\") {\n          testPushdown(\"fullouter\")\n        }\n      }\n    }\n  }\n\n  describe(\"predicates\") {\n    describe(\"and\") {\n      it(\"works with cast and true\") {\n        testQuery(\"select cast(owns_house as boolean) and true from users\")\n      }\n      it(\"works with cast and false\") {\n        testQuery(\"select cast(owns_house as boolean) and false from users\")\n      }\n      it(\"works with cast to bool\") {\n        testQuery(\"select cast(id as boolean) and cast(owns_house as boolean) from users\")\n      }\n      it(\"works with true and false\") {\n        testQuery(\"select true and false from users\")\n      }\n      it(\"works with null\") {\n        testQuery(\"select cast(id as boolean) and null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select cast(stringIdentity(id) as boolean) and cast(stringIdentity(owns_house) as boolean) from users\",\n          expectPartialPushdown = true)\n      }\n    }\n    describe(\"or\") {\n      it(\"works with cast or true\") {\n        testQuery(\"select cast(owns_house as boolean) or true from users\")\n      }\n      it(\"works with cast or false\") {\n        testQuery(\"select cast(owns_house as boolean) or false from users\")\n      }\n      it(\"works with cast to bool\") {\n        testQuery(\"select cast(id as boolean) or cast(owns_house as boolean) from users\")\n      }\n      it(\"works with true or false\") {\n        testQuery(\"select true or false from users\")\n      }\n      it(\"works with null\") {\n        testQuery(\"select cast(id as boolean) or null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\n          \"select cast(stringIdentity(id) as boolean) or cast(stringIdentity(owns_house) as boolean) from users\",\n          expectPartialPushdown = true)\n      }\n    }\n    describe(\"equal\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house = 1 as owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select id = '1' as owns_house from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select cast(owns_house as boolean) = true as owns_house from users\")\n      }\n      it(\"works with tinyint equals null\") {\n        testQuery(\"select owns_house = null as owns_house from users\")\n      }\n      it(\"works with null equals null\") {\n        testQuery(\"select null = null as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(id) = '1' from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"equalNullSafe\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house <=> 1 as owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select id <=> '1' as owns_house from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select cast(owns_house as boolean) <=> true as owns_house from users\")\n      }\n      it(\"works with tinyint equals null\") {\n        testQuery(\"select owns_house <=> null as owns_house from users\")\n      }\n      it(\"works with null equals null\") {\n        testQuery(\"select null <=> null as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(id) <=> '1' from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"lessThan\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house < 1 as owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select id < '10' as owns_house from users\")\n      }\n      it(\"works with text\") {\n        testQuery(\"select first_name < 'ZZZ' as first_name from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select cast(owns_house as boolean) < true as owns_house from users\")\n      }\n      it(\"works with tinyint less than null\") {\n        testQuery(\"select owns_house < null as owns_house from users\")\n      }\n      it(\"works with null less than null\") {\n        testQuery(\"select null < null as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(id) < '10' from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"lessThanOrEqual\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house <= 1 as owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select id <= '10' as owns_house from users\")\n      }\n      it(\"works with text\") {\n        testQuery(\"select first_name <= 'ZZZ' as first_name from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select cast(owns_house as boolean) <= true as owns_house from users\")\n      }\n      it(\"works with tinyint less than or equal null\") {\n        testQuery(\"select owns_house <= null as owns_house from users\")\n      }\n      it(\"works with null less than or equal null\") {\n        testQuery(\"select null <= null as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(id) <= '10' from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"greaterThan\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house > 1 as owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select id > '10' as owns_house from users\")\n      }\n      it(\"works with text\") {\n        testQuery(\"select first_name > 'ZZZ' as first_name from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select cast(owns_house as boolean) > false as owns_house from users\")\n      }\n      it(\"works with tinyint greater than null\") {\n        testQuery(\"select owns_house > null as owns_house from users\")\n      }\n      it(\"works with null greater than null\") {\n        testQuery(\"select null > null as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(id) > '10' from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"greaterThanOrEqual\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house >= 1 as owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select id >= '10' as owns_house from users\")\n      }\n      it(\"works with text\") {\n        testQuery(\"select first_name >= 'ZZZ' as first_name from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select cast(owns_house as boolean) >= true as owns_house from users\")\n      }\n      it(\"works with tinyint greater than or equal null\") {\n        testQuery(\"select owns_house >= null as owns_house from users\")\n      }\n      it(\"works with null greater than or equal null\") {\n        testQuery(\"select null >= null as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(id) >= '10' from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"in\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select owns_house in(1) as owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select id in('10','11','12') as owns_house from users\")\n      }\n      it(\"works with text\") {\n        testQuery(\"select first_name in('Wylie', 'Sukey', 'Sondra') as first_name from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select cast(owns_house as boolean) in(true) as owns_house from users\")\n      }\n      it(\"works with tinyint in null\") {\n        testQuery(\"select owns_house in(null) as owns_house from users\")\n      }\n      it(\"works with null in null\") {\n        testQuery(\"select null in(null) as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select stringIdentity(id) in('10') from users\", expectPartialPushdown = true)\n      }\n    }\n    describe(\"not\") {\n      it(\"works with tinyint\") {\n        testQuery(\"select not (owns_house = 1) as not_owns_house from users\")\n      }\n      it(\"works with single brackets\") {\n        testQuery(\"select not (id = '10') as owns_house from users\")\n      }\n      it(\"works with text\") {\n        testQuery(\"select not (first_name = 'Wylie') as first_name from users\")\n      }\n      it(\"works with boolean\") {\n        testQuery(\"select not (cast(owns_house as boolean) = true) as owns_house from users\")\n      }\n      it(\"works with tinyint not null\") {\n        testQuery(\"select not (owns_house = null) as owns_house from users\")\n      }\n      it(\"works with not null\") {\n        testQuery(\"select not (null = null) as null from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select not (stringIdentity(id) = '10') from users\", expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"same-name column selection\") {\n    it(\"join two tables which project the same column name\") {\n      testOrderedQuery(\n        \"select * from (select id from users) as a, (select id from movies) as b where a.id = b.id order by a.id\")\n    }\n    it(\"select same columns twice via natural join\") {\n      testOrderedQuery(\"select * from users as a natural join users order by a.id\")\n    }\n    it(\"select same column twice from table\") {\n      testQuery(\"select first_name, first_name from users\", expectPartialPushdown = true)\n    }\n    it(\"select same column twice from table with aliases\") {\n      testOrderedQuery(\"select first_name as a, first_name as a from users order by id\")\n    }\n    it(\"select same alias twice (different column) from table\") {\n      testOrderedQuery(\"select first_name as a, last_name as a from users order by id\")\n    }\n    it(\"select same column twice in subquery\") {\n      testQuery(\"select * from (select first_name, first_name from users) as x\",\n                expectPartialPushdown = true)\n    }\n    it(\"select same column twice from subquery with aliases\") {\n      testOrderedQuery(\n        \"select * from (select first_name as a, first_name as a from users order by id) as x\")\n    }\n  }\n\n  describe(\"datetimeExpressions\") {\n    describe(\"DateAdd\") {\n      it(\"positive num_days\") { testQuery(\"select date_add(birthday, age) from users\") }\n      it(\"negative num_days\") { testQuery(\"select date_add(birthday, -age) from users\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select date_add(stringIdentity(birthday), age) from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select date_add(birthday, -cast(stringIdentity(age) as int)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"DateSub\") {\n      it(\"positive num_days\") { testQuery(\"select date_sub(birthday, age) from users\") }\n      it(\"negative num_days\") { testQuery(\"select date_sub(birthday, -age) from users\") }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select date_sub(stringIdentity(birthday), age) from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select date_sub(birthday, -cast(stringIdentity(age) as int)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    val intervals = List(\n      \"1 year\",\n      \"1 month\",\n      \"3 week\",\n      \"2 day\",\n      \"7 hour\",\n      \"3 minute\",\n      \"5 second\",\n      \"1 year 1 month\",\n      \"1 week 3 hour 5 minute 4 seconds\"\n    )\n\n    describe(\"toUnixTimestamp\") {\n      it(\"works with TimestampType\") {\n        testQuery(\"select created, to_unix_timestamp(created) from reviews\")\n      }\n      it(\"works with DateType\") {\n        testQuery(\"select birthday, to_unix_timestamp(birthday) from users\")\n      }\n      it(\"partial pushdown because of udf\") {\n        testQuery(\"select to_unix_timestamp(stringIdentity(birthday)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"unixTimestamp\") {\n      it(\"works with TimestampType\") {\n        testQuery(\"select created, unix_timestamp(created) from reviews\")\n      }\n      it(\"works with DateType\") {\n        testQuery(\"select birthday, unix_timestamp(birthday) from users\")\n      }\n      it(\"partial pushdown because of udf\") {\n        testQuery(\"select unix_timestamp(stringIdentity(birthday)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"fromUnixTime\") {\n      it(\"works\") {\n        // cast is needed because in SingleStore 6.8 FROM_UNIXTIME query returns a result with microseconds\n        testQuery(\"select id, cast(from_unixtime(id) as timestamp) from movies\")\n      }\n      it(\"tutu\") {\n        testQuery(\"select from_unixtime(stringIdentity(id)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    // SingleStore and Spark differ on how they do last day calculations, so we ignore\n    // them in some of these tests\n\n    describe(\"timeAdd\") {\n      it(\"works\") {\n        for (interval <- intervals) {\n          println(s\"testing timeAdd with interval $interval\")\n          val query = s\"\"\"\n                       | select created, created + interval $interval\n                       | from reviews\n                       | where date(created) != last_day(created)\n                       |\"\"\".stripMargin\n          if (!interval.contains(\"day\") || spark.version != \"3.0.0\") {\n            testQuery(query)\n          }\n        }\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\n          s\"\"\"\n                     | select created, to_timestamp(stringIdentity(created)) + interval 1 month\n                     | from reviews\n                     | where date(created) != last_day(created)\n                     |\"\"\".stripMargin,\n          expectPartialPushdown = true\n        )\n      }\n    }\n\n    describe(\"timeSub\") {\n      it(\"works\") {\n        for (interval <- intervals) {\n          println(s\"testing timeSub with interval $interval\")\n          testQuery(s\"\"\"\n                       | select created, created - interval $interval\n                       | from reviews\n                       | where date(created) != last_day(created)\n                       |\"\"\".stripMargin)\n        }\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\n          s\"\"\"\n             | select created, to_timestamp(stringIdentity(created)) - interval 1 day\n             | from reviews\n             | where date(created) != last_day(created)\n             |\"\"\".stripMargin,\n          expectPartialPushdown = true\n        )\n      }\n    }\n\n    describe(\"addMonths\") {\n      it(\"works\") {\n        val numMonthsList = List(0, 1, 2, 12, 13, 200, -1, -2, -12, -13, -200)\n        for (numMonths <- numMonthsList) {\n          println(s\"testing addMonths with $numMonths months\")\n          testQuery(s\"\"\"\n                       | select created, add_months(created, $numMonths)\n                       | from reviews\n                       | where date(created) != last_day(created)\n                       |\"\"\".stripMargin)\n        }\n      }\n      it(\"partial pushdown with udf in the left argument\") {\n        testQuery(\n          s\"\"\"\n                     | select created, add_months(stringIdentity(created), 1)\n                     | from reviews\n                     | where date(created) != last_day(created)\n                     |\"\"\".stripMargin,\n          expectPartialPushdown = true\n        )\n      }\n      it(\"partial pushdown with udf in the right argument\") {\n        testQuery(\n          s\"\"\"\n             | select created, add_months(created, stringIdentity(1))\n             | from reviews\n             | where date(created) != last_day(created)\n             |\"\"\".stripMargin,\n          expectPartialPushdown = true\n        )\n      }\n    }\n\n    describe(\"NextDay\") {\n      it(\"works\") {\n        for ((dayOfWeek, _) <- ExpressionGen.DAYS_OF_WEEK_OFFSET_MAP) {\n          println(s\"testing nextDay with $dayOfWeek\")\n          testQuery(s\"\"\"\n                       | select created, next_day(created, '$dayOfWeek')\n                       | from reviews\n                       |\"\"\".stripMargin)\n        }\n      }\n      it(\"works with invalid day name\") {\n        testQuery(s\"\"\"\n                     | select created, next_day(created, 'invalid_day')\n                     | from reviews\n                     |\"\"\".stripMargin)\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select next_day(stringIdentity(created), 'monday') from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select next_day(created, stringIdentity('monday')) from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"DateDiff\") {\n      it(\"works\") {\n        testSingleReadForReadFromLeaves(\n          \"\"\"\n              | select birthday, created, DateDiff(birthday, created), DateDiff(created, birthday), DateDiff(created, created)\n              | from users inner join reviews on users.id = reviews.user_id\n              | \"\"\".stripMargin)\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select DateDiff(stringIdentity(created), created) from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select DateDiff(created, stringIdentity(created)) from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    // Spark doesn't support explicit time intervals like `+/-hh:mm`\n\n    val timeZones = List(\n      \"US/Mountain\",\n      \"Asia/Seoul\",\n      \"UTC\",\n      \"EST\",\n      \"Etc/GMT-6\"\n    )\n\n    describe(\"fromUTCTimestamp\") {\n      it(\"works\") {\n        for (timeZone <- timeZones) {\n          println(s\"testing fromUTCTimestamp with timezone $timeZone\")\n          testQuery(s\"select from_utc_timestamp(created, '$timeZone') from reviews\")\n        }\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select from_utc_timestamp(stringIdentity(created), 'EST') from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select from_utc_timestamp(created, stringIdentity('EST')) from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"toUTCTimestamp\") {\n      it(\"works\") {\n        for (timeZone <- timeZones) {\n          println(s\"testing toUTCTimestamp with timezone $timeZone\")\n          // singlestore doesn't support timestamps less then 1970-01-01T00:00:00Z\n          testQuery(\n            s\"select to_utc_timestamp(created, '$timeZone') from reviews where to_unix_timestamp(created) > 24*60*60\")\n        }\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\"select to_utc_timestamp(stringIdentity(created), 'EST') from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\"select to_utc_timestamp(created, stringIdentity('EST')) from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    // TruncTimestamp is called as date_trunc() in Spark\n    describe(\"truncTimestamp\") {\n      it(\"works\") {\n        val dateParts = List(\n          \"YEAR\",\n          \"YYYY\",\n          \"YY\",\n          \"MON\",\n          \"MONTH\",\n          \"MM\",\n          \"DAY\",\n          \"DD\",\n          \"HOUR\",\n          \"MINUTE\",\n          \"SECOND\",\n          \"WEEK\",\n          \"QUARTER\"\n        )\n        for (datePart <- dateParts) {\n          println(s\"testing truncTimestamp with datepart $datePart\")\n          testQuery(s\"select date_trunc('$datePart', created) from reviews\")\n        }\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(s\"select date_trunc(stringIdentity('DAY'), created) from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(s\"select date_trunc('DAY', stringIdentity(created)) from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    // TruncDate is called as trunc()\n    describe(\"truncDate\") {\n      it(\"works\") {\n        val dateParts = List(\"YEAR\", \"YYYY\", \"YY\", \"MON\", \"MONTH\", \"MM\")\n        for (datePart <- dateParts) {\n          println(s\"testing truncDate with datepart $datePart\")\n          testQuery(s\"select trunc(created, '$datePart') from reviews\")\n        }\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(s\"select trunc(stringIdentity(created), 'MONTH') from reviews\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(s\"select trunc(created, stringIdentity('MONTH')) from reviews\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"monthsBetween\") {\n      it(\"works\") {\n        for (interval <- intervals) {\n          println(s\"testing monthsBetween with interval $interval\")\n          testQuery(\n            s\"select months_between(created, created + interval $interval) from reviews\"\n          )\n        }\n      }\n      it(\"partial pushdown because of udf in the left argument\") {\n        testQuery(\n          s\"select months_between(stringIdentity(created), created + interval 1 month) from reviews\",\n          expectPartialPushdown = true)\n      }\n      it(\"partial pushdown because of udf in the right argument\") {\n        testQuery(\n          s\"select months_between(created, to_timestamp(stringIdentity(created)) + interval 1 month) from reviews\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    val periodsList: List[List[String]] = List(\n      List(\"YEAR\", \"Y\", \"YEARS\", \"YR\", \"YRS\"),\n      List(\"QUARTER\", \"QTR\"),\n      List(\"MONTH\", \"MON\", \"MONS\", \"MONTHS\"),\n      List(\"WEEK\", \"W\", \"WEEKS\"),\n      List(\"DAY\", \"D\", \"DAYS\"),\n      List(\"DAYOFWEEK\", \"DOW\"),\n      List(\"DAYOFWEEK_ISO\", \"DOW_ISO\"),\n      List(\"DOY\"),\n      List(\"HOUR\", \"H\", \"HOURS\", \"HR\", \"HRS\"),\n      List(\"MINUTE\", \"MIN\", \"M\", \"MINS\", \"MINUTES\")\n    )\n\n    it(\"extract\") {\n      for (periods <- periodsList) {\n        for (period <- periods) {\n          testQuery(s\"SELECT extract($period FROM birthday) as extract_period from users\")\n          testQuery(s\"SELECT extract($period FROM created) as extract_period from reviews\")\n        }\n      }\n    }\n\n    it(\"datePart\") {\n      for (periods <- periodsList) {\n        for (period <- periods) {\n          testQuery(s\"SELECT date_part('$period', birthday) as date_part from users\")\n          testQuery(s\"SELECT date_part('$period', created) as date_part from reviews\")\n        }\n      }\n    }\n\n    it(\"makeDate\") {\n      testQuery(\"SELECT make_date(1000, user_id, user_id) FROM reviews\")\n    }\n\n    it(\"makeTimestamp\") {\n      testQuery(\n        \"SELECT make_timestamp(1000, user_id, user_id, user_id, user_id, user_id) FROM reviews\")\n    }\n\n    it(\"CurrentDate\") {\n      testQuery(\"select current_date() from users\", expectSameResult = false)\n    }\n    it(\"Now\") {\n      testQuery(\"select now() from users\", expectSameResult = false)\n    }\n    it(\"DateFromUnixDate\") {\n      testQuery(\"select date_from_unix_date(1234567) from users\")\n    }\n    it(\"CurrentTimestamp\") {\n      testQuery(\"select current_timestamp() from users\", expectSameResult = false)\n    }\n\n    describe(\"UnixDate\") {\n      it(\"Simple query, one day after epoch\") {\n        testQuery(\"select unix_date(date('1970-01-02')) from users\")\n      }\n      it(\"works\") {\n        testQuery(\"select unix_date(birthday) from users\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select unix_date(date(stringIdentity(birthday))) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"unix seconds functions\") {\n      val functions = Seq(\"unix_seconds\", \"unix_micros\", \"unix_millis\")\n      it(\"works with timestamp\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(created) from reviews\")\n        }\n      }\n\n      it(\"partial pushdown\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(timestamp(stringIdentity(created))) from reviews\",\n                    expectPartialPushdown = true)\n        }\n      }\n    }\n\n    describe(\"converting (milli/micro)seconds to timestamp functions\") {\n      val functions = Seq(\"timestamp_seconds\", \"timestamp_millis\", \"timestamp_micros\")\n      it(\"works\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select created, $f(user_id) from reviews\")\n        }\n      }\n\n      it(\"works with negative data\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(-movie_id) from reviews\")\n        }\n      }\n\n      it(\"partial pushdown\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(int(stringIdentity(id))) from users\", expectPartialPushdown = true)\n        }\n      }\n    }\n\n    describe(\"timestamp parts functions\") {\n      val functions = Seq(\"Hour\",\n                          \"Minute\",\n                          \"Second\",\n                          \"DayOfYear\",\n                          \"Year\",\n                          \"Quarter\",\n                          \"Month\",\n                          \"DayOfMonth\",\n                          \"DayOfWeek\",\n                          \"WeekOfYear\",\n                          \"last_day\")\n      it(\"works with date\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(birthday) from users\")\n        }\n      }\n      it(\"works with timestamp\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(created) from reviews\")\n        }\n      }\n      it(\"works with string\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(first_name) from users\")\n        }\n      }\n      it(\"partial pushdown\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select $f(stringIdentity(first_name)) from users\",\n                    expectPartialPushdown = true)\n        }\n      }\n    }\n  }\n\n  describe(\"partial pushdown\") {\n    it(\"ignores spark UDFs\") {\n      spark.udf.register(\"myUpper\", (s: String) => s.toUpperCase)\n      testQuery(\"select myUpper(first_name), id from users where id in (10,11,12)\",\n                expectPartialPushdown = true)\n    }\n\n    it(\"join with pure-jdbc relation\") {\n      testSingleReadQuery(\n        \"\"\"\n        | select users.id, concat(first(users.first_name), \" \", first(users.last_name)) as full_name\n        | from users\n        | inner join testdb_jdbc.reviews on users.id = reviews.user_id\n        | group by users.id\n        | \"\"\".stripMargin,\n        expectPartialPushdown = true\n      )\n    }\n  }\n\n  describe(\"stringExpressions\") {\n    describe(\"StartsWith\") {\n      it(\"works\") {\n        testQuery(\"select * from movies\",\n                  filterDF = df => df.filter(df.col(\"critic_review\").startsWith(\"M\")))\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select stringIdentity(critic_review) as x from movies\",\n                  filterDF = df => df.filter(df.col(\"x\").startsWith(\"M\")),\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"EndsWith\") {\n      it(\"works\") {\n        testQuery(\"select * from movies\",\n                  filterDF = df => df.filter(df.col(\"critic_review\").endsWith(\"s.\")))\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select stringIdentity(critic_review) as x from movies\",\n                  filterDF = df => df.filter(df.col(\"x\").endsWith(\"s.\")),\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Contains\") {\n      it(\"works\") {\n        testQuery(\"select * from movies\",\n                  filterDF = df => df.filter(df.col(\"critic_review\").contains(\"a\")))\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select stringIdentity(critic_review) as x from movies\",\n                  filterDF = df => df.filter(df.col(\"x\").contains(\"a\")),\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringInstr\") {\n      it(\"works\") {\n        testQuery(\"select instr(critic_review, 'id') from movies\")\n      }\n      it(\"works when all arguments are not literals\") {\n        testQuery(\"select instr(critic_review, critic_review) from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select instr(stringIdentity(critic_review), 'id') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select instr(critic_review, stringIdentity('id')) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"FindInSet\") {\n      it(\"works\") {\n        testQuery(\"select id, find_in_set(id, '82, 1,13,54,28,39,42, owns_house,120') from users\")\n      }\n      it(\"constant example\") {\n        testQuery(\"select id, find_in_set('39', '1,2,3990, 13,28,39,42,54,82,120') from users\")\n      }\n      it(\"works with empty left argument\") {\n        testQuery(\"select find_in_set('', '1,2,3') from users\")\n      }\n      it(\"works with empty right argument\") {\n        testQuery(\"select find_in_set(id, '') from users\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\"select find_in_set(stringIdentity(critic_review), 'id') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select find_in_set(critic_review, stringIdentity('id')) from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"joinable object in the right argument\") {\n        testQuery(\"select find_in_set(critic_review, id) from movies\", expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringTrim\") {\n      it(\"works\") {\n        testQuery(\"select id, trim(first_name) from users\")\n      }\n      it(\"works when trimStr is ' '\") {\n        testQuery(\"select id, trim(both ' ' from first_name) from users\")\n      }\n      it(\"partial pushdown when trimStr is not None and not ' '\") {\n        testQuery(\"select id, trim(both 'abc' from first_name) from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, trim(stringIdentity(first_name)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringTrimLeft\") {\n      it(\"works\") {\n        testQuery(\"select id, ltrim(first_name) from users\")\n      }\n      it(\"works when trimStr is ' '\") {\n        testQuery(\"select id, trim(leading ' ' from first_name) from users\")\n      }\n      it(\"works when trimStr is ' ' (other syntax)\") {\n        testQuery(\"select id, ltrim(' ', first_name) from users\")\n      }\n      it(\"partial pushdown when trimStr is not None and not ' '\") {\n        testQuery(\"select id, ltrim('abc', first_name) from users\", expectPartialPushdown = true)\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, ltrim(stringIdentity(first_name)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringTrimRight\") {\n      it(\"works\") {\n        testQuery(\"select id, rtrim(first_name) from users\")\n      }\n      it(\"works when trimStr is ' '\") {\n        testQuery(\"select id, trim(trailing ' ' from first_name) from users\")\n      }\n      it(\"works when trimStr is ' ' (other syntax)\") {\n        testQuery(\"select id, rtrim(' ', first_name) from users\")\n      }\n      it(\"partial pushdown when trimStr is not None and not ' '\") {\n        testQuery(\"select id, rtrim('abc', first_name) from users\", expectPartialPushdown = true)\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, rtrim(stringIdentity(first_name)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"FormatNumber\") {\n      // singlestore and spark rounds fractional types differently\n      it(\"works with zero precision\") {\n        testQuery(\n          \"select format_number(critic_rating, 0) from movies where critic_rating - floor(critic_rating) != 0.5 or critic_rating is null\")\n      }\n      it(\"works with negative precision\") {\n        testQuery(\n          \"select format_number(critic_rating, -10) from movies where critic_rating - floor(critic_rating) != 0.5 or critic_rating is null\")\n      }\n      it(\"works with numbers and zero precision\") {\n        testQuery(\n          \"select format_number(id, 0) from movies where critic_rating - floor(critic_rating) != 0.5 or critic_rating is null\")\n      }\n      it(\"works with numbers and negative precision\") {\n        testQuery(\n          \"select format_number(id, -10) from movies where critic_rating - floor(critic_rating) != 0.5 or critic_rating is null\")\n      }\n      it(\"works with positive precision\") {\n        testQuery(\n          \"\"\"select format_number(critic_rating, cast(floor(critic_rating) as int)) as x from movies \n            |where (\n            |        critic_rating - floor(critic_rating) != 0.5 and \n            |        critic_rating*pow(10, floor(critic_rating))  - floor(critic_rating*pow(10, floor(critic_rating))) != 0.5\n            |    ) or \n            |    critic_rating is null\"\"\".stripMargin)\n      }\n      it(\"works with negative numbers\") {\n        testQuery(\n          \"\"\"select format_number(-critic_rating, 0), round(critic_rating, 5) as a from movies \n            |where (\n            |        critic_rating - floor(critic_rating) != 0.5 and \n            |        abs(critic_rating) > 1\n            |    ) or \n            |    critic_rating is null\"\"\".stripMargin)\n      }\n\n      it(\"works with format\") {\n        if (spark.version != \"2.3.4\") {\n          testQuery(\"select format_number(critic_rating, '#####,#,#,#.##') as x from movies\",\n                    expectPartialPushdown = true)\n        }\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\n          \"select format_number(cast(stringIdentity(critic_rating) as double), 4) from movies\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\"select format_number(critic_rating, cast(stringIdentity(4) as int)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringRepeat\") {\n      it(\"works\") {\n        testQuery(\"select id, repeat(critic_review, floor(critic_rating)) as x from movies\")\n      }\n      it(\"works with empty string\") {\n        testQuery(\"select id, repeat('', floor(critic_rating)) as x from movies\")\n      }\n      it(\"works with negative times\") {\n        testQuery(\n          \"select id, repeat(critic_review, -floor(critic_rating)) as x1, -floor(critic_rating)  as x2 from movies\")\n      }\n      it(\"udf in the left argument\") {\n        testQuery(\n          \"select id, repeat(stringIdentity(critic_review), -floor(critic_rating)) as x from movies\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the right argument\") {\n        testQuery(\n          \"select id, repeat(critic_review, -stringIdentity(floor(critic_rating))) as x from movies\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringReplace\") {\n      it(\"works\") {\n        testQuery(\"select id, replace(critic_review, 'an', 'AAA') from movies\")\n      }\n      it(\"works when second argument is empty\") {\n        testQuery(\"select id, replace(critic_review, '', 'A') from movies\")\n      }\n      it(\"works when third argument is empty\") {\n        testQuery(\"select id, replace(critic_review, 'a', '') from movies\")\n      }\n      it(\"works with two arguments\") {\n        testQuery(\"select id, replace(critic_review, 'a') from movies\")\n      }\n      it(\"works when all arguments are not literals\") {\n        testQuery(\"select id, replace(critic_review, title, genre) from movies\")\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select id, replace(stringIdentity(critic_review), 'an', 'AAA') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\"select id, replace(critic_review, stringIdentity('an'), 'AAA') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the third argument\") {\n        testQuery(\"select id, replace(critic_review, 'an', stringIdentity('AAA')) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringOverlay\") {\n      it(\"works\") {\n        testQuery(\"select id, overlay(email placing '#Glory_To_Ukraine#' from 9) from users\")\n      }\n      it(\"works with non-empty len\") {\n        testQuery(\"select id, overlay(email placing '#Ukraine#' from 9 for 3) from users\")\n      }\n      it(\"works with comma separated arguments\") {\n        testQuery(\"select id, overlay(email, '#Glory_To_Ukraine#', 9, 6) from users\")\n      }\n      it(\"works when second argument is not literal\") {\n        testQuery(\"select id, overlay(email, last_name, 3, 4) from users\")\n      }\n      it(\"works with negative len\") {\n        testQuery(\"select id, overlay(email placing '#Glory_To_Ukraine#' from 9 for -3) from users\")\n      }\n      it(\"works with negative position\") {\n        testQuery(\"select id, overlay(email placing first_name from -2 for 0) from users_sample\")\n      }\n      it(\"works with len is zero\") {\n        testQuery(\"select id, overlay(email, '#Heroyam_Slava#', 3, 0) from users\")\n      }\n      it(\"works with len is negative\") {\n        testQuery(\"select id, overlay(email, '#SLAVA_ZSY#', 3, -4) from users\")\n      }\n      it(\"works with len is a string\") {\n        testQuery(\"select id, overlay(email, '#SLAVA_ZSY#', 3, '2') from users\")\n      }\n      it(\"works with len is greater than len of replacing string\") {\n        testQuery(\"select id, overlay(email placing last_name from 3 for 10) from users\")\n      }\n      it(\"works with pos is greater than input string string\") {\n        testQuery(\"select id, overlay(email, '#Slava_Ukraini#', 70, 3) from users\")\n      }\n      it(\"works when pos is not literal\") {\n        testQuery(\"select id, overlay(email, '#PTHPNH#', age) from users\")\n      }\n      it(\"works when len is not literal\") {\n        testQuery(\"select id, overlay(email, '#Heroyam_Slava#', 3, id) from users\")\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select id, overlay(stringIdentity(email), '#Heroyam_Slava#', 3, 4) from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the 'pos' argument\") {\n        testQuery(\n          \"select id, overlay(email, '#StandWithUkraine#', stringIdentity(age), 4) from users\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the 'len' argument\") {\n        testQuery(\"select id, overlay(email, '#ZePresident#', 3, stringIdentity(id)) from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"SubstringIndex\") {\n      it(\"works with negative count\") {\n        testQuery(\"select id, substring_index(critic_review, ' ', -100) from movies\")\n      }\n      it(\"works with zero count\") {\n        testQuery(\"select id, substring_index(critic_review, ' ', 0) from movies\")\n      }\n      it(\"works with small count\") {\n        testQuery(\"select id, substring_index(critic_review, ' ', 2) from movies\")\n      }\n      it(\"works with large count\") {\n        testQuery(\"select id, substring_index(critic_review, ' ', 100) from movies\")\n      }\n      it(\"works when delimiter and count are not literals\") {\n        testQuery(\"select id, substring_index(critic_review, title, id) from movies\")\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\n          \"select id, substring_index(stringIdentity(critic_review), 'an', '2') from movies\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\"select id, substring_index(critic_review, stringIdentity(' '), '2') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the third argument\") {\n        testQuery(\"select id, substring_index(critic_review, ' ', stringIdentity(2)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringLocate\") {\n      it(\"works with negative start\") {\n        testQuery(\"select id, locate(critic_review, ' ', -100) from movies\")\n      }\n      it(\"works with zero start\") {\n        testQuery(\"select id, locate(critic_review, ' ', 0) from movies\")\n      }\n      it(\"works with small start\") {\n        testQuery(\"select id, locate(critic_review, ' ', 2) from movies\")\n      }\n      it(\"works with large start\") {\n        testQuery(\"select id, locate(critic_review, ' ', 100) from movies\")\n      }\n      it(\"works when str and start are not literals\") {\n        testQuery(\"select id, locate(critic_review, title, id) from movies\")\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select id, locate(stringIdentity(critic_review), 'an', '2') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\"select id, locate(critic_review, stringIdentity(' '), '2') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the third argument\") {\n        testQuery(\"select id, locate(critic_review, ' ', stringIdentity(2)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringLPad\") {\n      it(\"works with negative len\") {\n        testQuery(\"select id, lpad(critic_review, -100, 'ab') from movies\")\n      }\n      it(\"works with zero len\") {\n        testQuery(\"select id, lpad(critic_review, 0, 'ab') from movies\")\n      }\n      it(\"works with small len\") {\n        testQuery(\"select id, lpad(critic_review, 3, 'ab') from movies\")\n      }\n      it(\"works with large len\") {\n        testQuery(\"select id, lpad(critic_review, 1000, 'ab') from movies\")\n      }\n      it(\"works when len and pad are not literals\") {\n        testQuery(\"select id, lpad(critic_review, id, title) from movies\")\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select id, lpad(stringIdentity(critic_review), 2, 'an') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\"select id, lpad(critic_review, stringIdentity(2), ' ') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the third argument\") {\n        testQuery(\"select id, lpad(critic_review, 2, stringIdentity(' ')) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringRPad\") {\n      it(\"works with negative len\") {\n        testQuery(\"select id, rpad(critic_review, -100, 'ab') from movies\")\n      }\n      it(\"works with zero len\") {\n        testQuery(\"select id, rpad(critic_review, 0, 'ab') from movies\")\n      }\n      it(\"works with small len\") {\n        testQuery(\"select id, rpad(critic_review, 3, 'ab') from movies\")\n      }\n      it(\"works with large len\") {\n        testQuery(\"select id, rpad(critic_review, 1000, 'ab') from movies\")\n      }\n      it(\"works when len and pad are not literals\") {\n        testQuery(\"select id, rpad(critic_review, id, title) from movies\")\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select id, rpad(stringIdentity(critic_review), 2, 'an') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\"select id, rpad(critic_review, stringIdentity(2), ' ') from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the third argument\") {\n        testQuery(\"select id, rpad(critic_review, 2, stringIdentity(' ')) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Substring\") {\n      it(\"works\") {\n        testQuery(\"select id, substring(critic_review, 1, 20) from movies\")\n      }\n      it(\"works when pos is negative\") {\n        testQuery(\"select id, substring(critic_review, -1, 20) from movies\")\n      }\n      it(\"works with empty len\") {\n        testQuery(\"select id, substring(critic_review, 5) from movies\")\n      }\n      it(\"works with negative len\") {\n        testQuery(\"select id, substring(critic_review, 5, -4) from movies\")\n      }\n      it(\"works with non-literals\") {\n        testQuery(\"select id, substring(critic_review, id, id) from movies\")\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select id, substring(stringIdentity(critic_review), 5, 4) from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\"select id, substring(critic_review, stringIdentity(5), 4) from movies\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the third argument\") {\n        testQuery(\"select id, substring(critic_review, 5, stringIdentity(4)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Initcap\") {\n      it(\"works\") {\n        // Here we don't expect same result as spark implementation of `initcap` method\n        // does not define as the beginning of a word that is enclosed in quotation marks / brackets, etc.\n        testQuery(\"select id, initcap(critic_review) from movies\", expectSameResult = false)\n      }\n      it(\"same result on simple text\") {\n        testQuery(\"select id, initcap(favorite_color) from users\")\n      }\n      it(\"works with ints\") {\n        testQuery(\"select id, initcap(id) from movies\")\n      }\n      it(\"partial pushdown whith udf\") {\n        testQuery(\"select id, initcap(stringIdentity(critic_review)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringTranslate\") {\n      it(\"works\") {\n        testQuery(\"select id, translate(email, 'com', '123') from users\")\n      }\n      it(\"works when 'from' argument is longer than 'to'\") {\n        testQuery(\"select id, translate(email, 'coma', '123') from users\")\n      }\n      it(\"works when 'to' argument is longer than 'from'\") {\n        testQuery(\"select id, translate(email, 'com', '1234') from users\")\n      }\n      it(\"works when 'from' argument is empty\") {\n        testQuery(\"select id, translate(email, '', '123') from users\")\n      }\n      it(\"works when 'to' argument is empty\") {\n        testQuery(\"select id, translate(email, 'abb', '') from users\")\n      }\n      it(\"works when both 'from' and 'to' arguments are empty\") {\n        testQuery(\"select id, translate(email, '', '') from users\")\n      }\n      it(\"partial pushdown when the second argument is not literal\") {\n        testQuery(\"select id, translate(email, last_name, '1234') from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown when the third argument is not literal\") {\n        testQuery(\"select id, translate(email, 'com', last_name) from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\"select id, translate(stringIdentity(email), '@', '#') from users\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Upper\") {\n      it(\"works\") {\n        testQuery(\"select id, upper(critic_review) from movies\")\n      }\n      it(\"works with ints\") {\n        testQuery(\"select id, upper(id) from movies\")\n      }\n      it(\"partial pushdown whith udf\") {\n        testQuery(\"select id, upper(stringIdentity(critic_review)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Lower\") {\n      it(\"works\") {\n        testQuery(\"select id, lower(critic_review) from movies\")\n      }\n      it(\"works with ints\") {\n        testQuery(\"select id, lower(id) from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, lower(stringIdentity(critic_review)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Left\") {\n      it(\"works\") {\n        testQuery(\"select id, left(last_name, 2) as l from users_sample\")\n      }\n      it(\"works with len is sting \") {\n        testQuery(\"select id, left(last_name, '4') as l from users_sample\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, left(stringIdentity(critic_review), 4) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Right\") {\n      it(\"works\") {\n        testQuery(\"select id, right(critic_review, 2) as r from movies\")\n      }\n      it(\"works with len is sting\") {\n        testQuery(\"select id, right(critic_review, '4') as r from movies\")\n      }\n      it(\"partial pushdown whith udf\") {\n        testQuery(\"select id, right(stringIdentity(critic_review), 4) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Concat_ws\") {\n      it(\"works\") {\n        testQuery(\"select id, CONCAT_WS('@', id, 'user.com') as conc from movies\")\n      }\n      it(\"works with many expressions\") {\n        testQuery(\n          \"select id, CONCAT_WS('@', last_name, 'singlestore', '.', 'com', id) as conc from users\")\n      }\n      it(\"partial pushdown whith udf\") {\n        testQuery(\n          \"select id, CONCAT_WS('@', stringIdentity(critic_review), 'user.com') as conc from movies\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"StringSpace\") {\n      it(\"works\") {\n        testQuery(\"select id, space(floor(critic_rating)) as x from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, space(stringIdentity(id)) from movies\", expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Length\") {\n      it(\"works with strings\") {\n        testQuery(\"select id, length(critic_review) from movies\")\n      }\n      it(\"works with binary\") {\n        testQuery(\"select id, length(cast(critic_review as binary)) from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, length(stringIdentity(id)) from movies\", expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"BitLength\") {\n      it(\"works with strings\") {\n        testQuery(\"select id, bit_length(critic_review) from movies\")\n      }\n      it(\"works with binary\") {\n        testQuery(\"select id, bit_length(cast(critic_review as binary)) from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, bit_length(stringIdentity(id)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"OctetLength\") {\n      it(\"works with strings\") {\n        testQuery(\"select id, octet_length(critic_review) from movies\")\n      }\n      it(\"works with binary\") {\n        testQuery(\"select id, octet_length(cast(critic_review as binary)) from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, octet_length(stringIdentity(id)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Ascii\") {\n      it(\"works\") {\n        testQuery(\"select id, ascii(critic_review) from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, ascii(stringIdentity(critic_review)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Chr\") {\n      it(\"works\") {\n        testQuery(\"select id, chr(floor(critic_rating)) as ch from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, chr(stringIdentity(id)) from movies\", expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"Base64\") {\n      it(\"works\", ExcludeFromSpark33, ExcludeFromSpark34, ExcludeFromSpark35, ExcludeFromSpark40) {\n        testQuery(\"select id, base64(critic_review) as x from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, base64(stringIdentity(critic_review)) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"UnBase64\") {\n      it(\"works\", ExcludeFromSpark33, ExcludeFromSpark34, ExcludeFromSpark35, ExcludeFromSpark40) {\n        testQuery(\"select id, unbase64(base64(critic_review)) as x from movies\")\n      }\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select id, unbase64(base64(stringIdentity(critic_review))) from movies\",\n                  expectPartialPushdown = true)\n      }\n    }\n  }\n\n  describe(\"decimalExpressions\") {\n    val precisions = List(2, 5, Decimal.MAX_LONG_DIGITS - 10)\n    val scales     = List(1, 4, Decimal.MAX_LONG_DIGITS - 11)\n    it(\"sum of decimals\") {\n      // If precision + 10 <= Decimal.MAX_LONG_DIGITS then DecimalAggregates optimizer will add MakeDecimal and UnscaledValue to this query\n      for (precision <- precisions;\n           // If rating >= 10^(precision - scale) then rating will overflow during the casting\n           // JDBC returns null on overflow if !ansiEnabled and errors otherwise\n           // SingleStore truncates the value on overflow\n           // Because of this, we skip the case when scale is equals to precision (all rating values are less then 10)\n           scale <- scales if scale < precision) {\n        testSingleReadForOldS2(\n          s\"select sum(cast(rating as decimal($precision, $scale))) as rs from reviews\",\n          SinglestoreVersion(7, 6, 0))\n      }\n\n    }\n\n    it(\"window expression with sum of decimals\") {\n      // If precision + 10 <= Decimal.MAX_LONG_DIGITS then DecimalAggregates optimizer will add MakeDecimal and UnscaledValue to this query\n      for (precision <- precisions;\n           // If rating >= 10^(precision - scale) then rating will overflow during the casting\n           // JDBC returns null on overflow if !ansiEnabled and errors otherwise\n           // SingleStore truncates the value on overflow\n           // Because of this, we skip the case when scale is equals to precision (all rating values are less then 10)\n           scale <- scales if scale < precision) {\n        testSingleReadForReadFromLeaves(\n          s\"select sum(cast(rating as decimal($precision, $scale))) over (order by rating) as out from reviews\")\n      }\n    }\n\n    it(\"avg of decimals\") {\n      // If precision + 4 <= MAX_DOUBLE_DIGITS (15) then DecimalAggregates optimizer will add MakeDecimal and UnscaledValue to this query\n      for (precision <- precisions;\n           // If rating >= 10^(precision - scale) then rating will overflow during the casting\n           // JDBC returns null on overflow if !ansiEnabled and errors otherwise\n           // SingleStore truncates the value on overflow\n           // Because of this, we skip the case when scale is equals to precision (all rating values are less then 10)\n           scale <- scales if scale < precision) {\n        testSingleReadForOldS2(\n          s\"select avg(cast(rating as decimal($precision, $scale))) as rs from reviews\",\n          SinglestoreVersion(7, 6, 0))\n      }\n    }\n\n    it(\"window expression with avg of decimals\") {\n      // If precision + 4 <= MAX_DOUBLE_DIGITS (15) then DecimalAggregates optimizer will add MakeDecimal and UnscaledValue to this query\n      for (precision <- precisions;\n           // If rating >= 10^(precision - scale) then rating will overflow during the casting\n           // JDBC returns null on overflow if !ansiEnabled and errors otherwise\n           // SingleStore truncates the value on overflow\n           // Because of this, we skip the case when scale is equals to precision (all rating values are less then 10)\n           scale <- scales if scale < precision) {\n        testSingleReadForReadFromLeaves(\n          s\"select avg(cast(rating as decimal($precision, $scale))) over (order by rating) as out from reviews\")\n      }\n    }\n  }\n\n  describe(\"SortOrder\") {\n    it(\"works asc, nulls first\") {\n      testOrderedQuery(\"select * from movies order by critic_rating asc nulls first, id\")\n    }\n    it(\"works desc, nulls last\") {\n      testOrderedQuery(\"select * from movies order by critic_rating desc nulls last, id\")\n    }\n    it(\"partial pushdown asc, nulls last\") {\n      testSingleReadQuery(\"select * from movies order by critic_rating asc nulls last\",\n                          expectPartialPushdown = true)\n    }\n    it(\"partial pushdown desc, nulls first\") {\n      testSingleReadQuery(\"select * from movies order by critic_rating desc nulls first\",\n                          expectPartialPushdown = true)\n    }\n    it(\"partial pushdown with udf\") {\n      testSingleReadQuery(\"select * from movies order by stringIdentity(critic_rating)\",\n                          expectPartialPushdown = true)\n    }\n  }\n\n  describe(\"Rand\") {\n    it(\"integer literal\") {\n      testQuery(\"select rand(100)*id from users\", expectSameResult = false)\n    }\n    it(\"long literal\") {\n      testQuery(\"select rand(100L)*id from users\", expectSameResult = false)\n    }\n    it(\"null literal\") {\n      testQuery(\"select rand(null)*id from users\", expectSameResult = false)\n    }\n\n    it(\"should return the same value for the same input\") {\n      val df1 = spark.sql(\"select rand(100)*id from (select id from testdb.users order by id)\")\n      val df2 = spark.sql(\"select rand(100)*id from (select id from testdb.users order by id)\")\n      assertApproximateDataFrameEquality(df1, df2, 0.001, orderedComparison = false)\n    }\n  }\n\n  describe(\"regular expressions\") {\n    describe(\"like\") {\n      it(\"simple\") {\n        testQuery(\"select * from users where first_name like 'Di'\")\n      }\n      it(\"simple, both fields\") {\n        testQuery(\"select * from users where first_name like last_name\")\n      }\n      it(\"character wildcard\") {\n        testQuery(\"select * from users where first_name like 'D_'\")\n      }\n      it(\"string wildcard\") {\n        testQuery(\"select * from users where first_name like 'D%'\")\n      }\n      it(\"dumb true\") {\n        testQuery(\"select * from users where 1 like 1\")\n      }\n      it(\"dumb false\") {\n        testQuery(\"select 2 like 1 from users\")\n      }\n      it(\"dumb true once more\") {\n        testQuery(\"select first_name from users where first_name like first_name\")\n      }\n      it(\"partial pushdown left\") {\n        testQuery(\"select * from users where stringIdentity(first_name) like 'Di'\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown right\") {\n        testQuery(\"select * from users where first_name like stringIdentity('Di')\",\n                  expectPartialPushdown = true)\n      }\n      it(\"null\") {\n        testQuery(\"select critic_review like null from movies\")\n      }\n      it(\"with escape char\") {\n        testQuery(\n          \"select first_name like last_name escape '^', last_name like first_name escape '^' from users\")\n      }\n      it(\"with escape char equal to '/'\") {\n        testQuery(\n          \"select first_name like last_name escape '/', last_name like first_name escape '/' from users\")\n      }\n    }\n    describe(\"rlike\") {\n      it(\"simple\") {\n        testQuery(\"select * from users where first_name rlike 'D.'\")\n      }\n      it(\"simple, both fields\") {\n        testQuery(\"select * from users where first_name rlike last_name\")\n      }\n      it(\"from beginning\") {\n        testQuery(\"select * from users where first_name rlike '^D.'\")\n      }\n      it(\"dumb true\") {\n        testQuery(\"select * from users where 1 rlike 1\")\n      }\n      it(\"dumb false\") {\n        testQuery(\"select 2 rlike 1 from users\")\n      }\n      it(\"partial pushdown left\") {\n        testQuery(\"select * from users where stringIdentity(first_name) rlike 'D.'\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown right\") {\n        testQuery(\"select * from users where first_name rlike stringIdentity('D.')\",\n                  expectPartialPushdown = true)\n      }\n      it(\"null\") {\n        testQuery(\"select critic_review rlike null from movies\")\n      }\n    }\n\n    describe(\"(not) like all/any patterns functions\") {\n      val functions = Seq(\"like all\", \"like any\", \"not like all\", \"not like any\")\n      it(\"simple\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select id, first_name from users where first_name $f ('An%te%')\")\n        }\n      }\n      it(\"simple, both fields\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select id, first_name from users where first_name $f (last_name, 'Al%')\")\n        }\n      }\n      it(\"repeated pattern\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(\n            s\"select id, first_name from users where first_name $f ('Al%', last_name, 'Al%')\")\n        }\n      }\n      it(\"character wildcard\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select * from users where first_name $f ('A___e', '_n__e')\")\n        }\n      }\n      it(\"string wildcard\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select * from users where first_name $f ('Kon%ce', '%tan%', '%Kon%tan%ce%')\")\n        }\n      }\n      it(\"dumb true\") {\n        for (i <- 0 to 1) {\n          log.debug(s\"testing ${functions(i)}\")\n          testQuery(s\"select * from users where '1' ${functions(i)} ('1')\")\n        }\n      }\n      it(\"dumb false\") {\n        for (i <- 0 to 1) {\n          log.debug(s\"testing ${functions(i)}\")\n          testQuery(s\"select * from users where id ${functions(i)} ('D%', 'A%bbbb%')\",\n                    expectEmpty = true)\n        }\n        for (i <- 2 to 3) {\n          log.debug(s\"testing ${functions(i)}\")\n          testQuery(s\"select * from users where id ${functions(i)} ('D%', 'A%bbbb%')\")\n        }\n      }\n      it(\"dumb true once more\") {\n        for (i <- 0 to 1) {\n          log.debug(s\"testing ${functions(i)}\")\n          testQuery(s\"select * from users where first_name ${functions(i)} (first_name)\")\n        }\n        for (i <- 2 to 3) {\n          log.debug(s\"testing ${functions(i)}\")\n          testQuery(s\"select * from users where first_name ${functions(i)} (first_name)\",\n                    expectEmpty = true)\n        }\n      }\n      it(\"null\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select critic_review $f (null) from movies\")\n        }\n      }\n      it(\"partial pushdown left\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select * from users where stringIdentity(first_name) $f ('Ali%')\",\n                    expectPartialPushdown = true)\n        }\n      }\n      it(\"partial pushdown right\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select * from users where first_name $f (stringIdentity('Ali%'))\",\n                    expectPartialPushdown = true)\n        }\n      }\n      it(\"very simple patterns\", ExcludeFromSpark34, ExcludeFromSpark35, ExcludeFromSpark40) {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          //Sparks computes such in more optimal way and does not invoke pushdown\n          testQuery(s\"select * from users where first_name $f ('A%', '%b%', '%e')\",\n                    expectPartialPushdown = true)\n        }\n      }\n      it(\"very simple patterns full pushdown\",\n         ExcludeFromSpark31,\n         ExcludeFromSpark32,\n         ExcludeFromSpark33) {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          testQuery(s\"select * from users where first_name $f ('A%', '%b%', '%e')\")\n        }\n      }\n      it(\"empty patterns arg\") {\n        for (f <- functions) {\n          log.debug(s\"testing $f\")\n          try {\n            testQuery(s\"select * from users where first_name $f ()\", expectPartialPushdown = true)\n          } catch {\n            case e: Throwable =>\n              if (e.toString.contains(\"Expected something between '(' and ')'\")) {\n                None\n              } else {\n                throw e\n              }\n          }\n        }\n      }\n    }\n\n    describe(\"regexp\") {\n      it(\"simple\") {\n        testQuery(\"select * from users where first_name regexp 'D.'\")\n      }\n      it(\"simple, both fields\") {\n        testQuery(\"select * from users where first_name regexp last_name\")\n      }\n      it(\"dumb true\") {\n        testQuery(\"select * from users where 1 regexp 1\")\n      }\n      it(\"dumb false\") {\n        testQuery(\"select 2 regexp 1 from users\")\n      }\n      it(\"partial pushdown left\") {\n        testQuery(\"select * from users where stringIdentity(first_name) regexp 'D.'\",\n                  expectPartialPushdown = true)\n      }\n      it(\"partial pushdown right\") {\n        testQuery(\"select * from users where first_name regexp stringIdentity('D.')\",\n                  expectPartialPushdown = true)\n      }\n      it(\"null\") {\n        testQuery(\"select critic_review regexp null from movies\")\n      }\n    }\n\n    describe(\"regexpReplace\") {\n      it(\"simple\") {\n        testQuery(\"select regexp_replace(first_name, 'D', 'd') from users\")\n      }\n      it(\"works correctly\") {\n        testQuery(\"select * from users where regexp_replace(first_name, 'D', 'd') = 'di'\")\n      }\n      it(\"partial pushdown\") {\n        testQuery(\"select regexp_replace(stringIdentity(first_name), 'D', 'd') from users\",\n                  expectPartialPushdown = true)\n      }\n      it(\"null\") {\n        testQuery(\"select regexp_replace(title, 'D', critic_review) from movies\")\n      }\n      it(\"non-literals\") {\n        testQuery(\"select regexp_replace(first_name, first_name, first_name) from users\")\n      }\n      it(\"with position\") {\n        testQuery(\"select regexp_replace(first_name, 'a', 'd', 3) from users\")\n      }\n      it(\"big position\") {\n        testQuery(\"select regexp_replace(first_name, 'a', 'd', 100) from users\")\n      }\n    }\n  }\n\n  describe(\"automaticLite parallel read\") {\n    it(\"no sort in the query\") {\n      testQuery(\"select id from users\")\n    }\n\n    it(\"non top-level sort\") {\n      testQuery(\n        \"select id from (select id from users order by id) group by id\",\n        expectSingleRead = !canDoParallelReadFromAggregators\n      )\n    }\n\n    it(\"top-level sort\") {\n      testQuery(\"select id from users order by id\", expectSingleRead = true, alreadyOrdered = true)\n    }\n  }\n\n  describe(\"json functions\") {\n    describe(\"GetJsonObject\") {\n      it(\"works with strings\") {\n        testQuery(\n          \"select id, get_json_object(movie_rating, '$.title') as get_j from movies_rating\"\n        )\n      }\n      it(\"works with int\") {\n        testQuery(\n          \"select id, get_json_object(movie_rating, '$.movie_id') as get_j from movies_rating\")\n      }\n      it(\"works with booleans\") {\n        testQuery(\"select id, get_json_object(movie_rating, '$.3D') as get_j from movies_rating\")\n      }\n      it(\"works with arrays\") {\n        testQuery(\n          \"select id, get_json_object(movie_rating, '$.reviews') as get_j from movies_rating\")\n      }\n      it(\"works with nested json\") {\n        testQuery(\n          \"select id, get_json_object(movie_rating, '$.timetable.hall') as get_j from movies_rating\")\n      }\n      it(\"works with nested json string\") {\n        testQuery(\n          \"select id, get_json_object(movie_rating, '$.timetable.day') as get_j from movies_rating\")\n      }\n      it(\"non-existing  path\") {\n        testQuery(\n          \"select id, get_json_object(movie_rating, '$.nonexistingPath') as get_j from movies_rating\")\n      }\n      it(\"invalid path\") {\n        testQuery(\"select id, get_json_object(movie_rating, 'rating') as get_j from movies_rating\",\n                  expectPartialPushdown = true)\n      }\n      it(\"udf in the first argument\") {\n        testQuery(\n          \"select id, get_json_object(stringIdentity(movie_rating), '$.title') as get_j from movies_rating\",\n          expectPartialPushdown = true)\n      }\n      it(\"udf in the second argument\") {\n        testQuery(\n          \"select id, get_json_object(movie_rating, stringIdentity('$.title')) as get_j from movies_rating\",\n          expectPartialPushdown = true)\n      }\n    }\n\n    describe(\"LengthOfJsonArray\") {\n      it(\"works\") {\n        testQuery(\"select id, json_array_length(same_rate_movies) from movies_rating\")\n      }\n      it(\"udf\") {\n        testQuery(\n          \"select id, json_array_length(stringIdentity(same_rate_movies)) from movies_rating\",\n          expectPartialPushdown = true)\n      }\n\n    }\n  }\n\n  describe(\"misc functions\") {\n    def testUUIDPushdown(q: String): Unit = {\n      spark.sql(\"use testdb_jdbc\")\n      val jdbcDF = spark.sql(q)\n\n      jdbcDF.collect()\n      spark.sql(\"use testdb\")\n      val singlestoreDF = spark.sql(q)\n      assert(singlestoreDF.schema.equals(jdbcDF.schema))\n\n      val uuidPattern: Regex =\n        \"[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}\".r\n      val uuidFieldIndex = singlestoreDF.schema.fieldIndex(\"uuid()\")\n\n      uuidPattern.findFirstMatchIn(singlestoreDF.rdd.first().getString(uuidFieldIndex)) match {\n        case Some(_) => None\n        case None =>\n          throw new IllegalArgumentException(\n            \"Invalid format of an universally unique identifier (UUID) string generated by Singlestore client\"\n          )\n      }\n\n      uuidPattern.findFirstMatchIn(jdbcDF.rdd.first().getString(uuidFieldIndex)) match {\n        case Some(_) => None\n        case None =>\n          throw new IllegalArgumentException(\n            \"Invalid format of an universally unique identifier (UUID) string generated by Spark\"\n          )\n      }\n    }\n\n    describe(\"UUID\") {\n      it(\"correct query\") {\n        if (version.atLeast(SinglestoreVersion(7, 5, 0))) {\n          testQuery(\"select uuid(), id from users_sample\", expectSameResult = false)\n          testUUIDPushdown(\"select uuid(), id from users_sample\")\n        } else {\n          testQuery(\"select uuid(), id from users_sample\",\n                    expectSameResult = false,\n                    expectPartialPushdown = true)\n        }\n      }\n\n      it(\"partial pushdown with udf\") {\n        testQuery(\"select uuid(), stringIdentity(first_name) from users\",\n                  expectPartialPushdown = true,\n                  expectSameResult = false)\n        testUUIDPushdown(\"select uuid(), stringIdentity(first_name) from users\")\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/SanityTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.SQLSyntaxErrorException\nimport com.github.mrpowers.spark.daria.sql.SparkSessionExt._\nimport com.singlestore.spark.SinglestoreOptions.{CompressionType, TableKeyType}\nimport org.apache.spark.sql.functions._\nimport org.apache.spark.sql.types.{IntegerType, LongType, StringType, StructField, StructType}\nimport org.apache.spark.sql.{DataFrame, SaveMode, SparkSession}\nimport org.scalatest.BeforeAndAfterEach\nimport com.singlestore.spark.SQLHelper._\n\nclass SanityTest extends IntegrationSuiteBase with BeforeAndAfterEach {\n  var df: DataFrame = _\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n\n    df = spark.createDF(\n      List((1, \"Albert\"), (5, \"Ronny\"), (7, \"Ben\"), (9, \"David\")),\n      List((\"id\", IntegerType, true), (\"name\", StringType, true))\n    )\n    writeTable(\"testdb.foo\", df)\n  }\n\n  it(\"sets strict sql session variables\") {\n    // set a large but not exactly the same sql select limit\n    executeQueryWithLog(\"set global sql_select_limit = 18446744000000000000\")\n\n    val variables = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .load(\"information_schema.session_variables\")\n      .collect()\n      .groupBy(r => r.get(0))\n      .mapValues(r => r.map(_.getString(1)).head)\n\n    assert(variables(\"SQL_SELECT_LIMIT\") == \"18446744073709551615\")\n    assert(variables(\"COMPILE_ONLY\") == \"OFF\")\n\n    val sql_mode = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(SinglestoreOptions.QUERY, \"select @@sql_mode\")\n      .load()\n      .collect()\n      .head\n      .getString(0)\n    assert(sql_mode == \"ONLY_FULL_GROUP_BY,STRICT_ALL_TABLES\")\n  }\n\n  it(\"DataSource V1 read sanity custom schema\") {\n    val schema = StructType(\n      Seq(\n        StructField(\"id\", LongType, true),\n        StructField(\"name\", StringType, true),\n      ))\n\n    val x = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(SinglestoreOptions.TABLE_NAME, \"testdb.foo\")\n      .option(\"disablePushdown\", \"true\")\n      .option(\"customSchema\", schema.toDDL)\n      .load()\n\n    assertSmallDataFrameEquality(\n      x,\n      df.withColumn(\"id\", df(\"id\").cast(LongType)),\n      orderedComparison = false\n    )\n  }\n\n  it(\"DataSource V1 read sanity\") {\n    val x = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(SinglestoreOptions.TABLE_NAME, \"testdb.foo\")\n      .load()\n      .withColumn(\"hello\", lit(2))\n      .filter(col(\"id\") > 1)\n      .limit(1000)\n      .groupBy(col(\"id\"))\n      .agg(count(\"*\"))\n\n    assertSmallDataFrameEquality(\n      x,\n      df.withColumn(\"hello\", lit(2))\n        .filter(col(\"id\") > 1)\n        .limit(1000)\n        .groupBy(col(\"id\"))\n        .agg(count(\"*\")),\n      orderedComparison = false\n    )\n  }\n\n  it(\"DataSource V1 write sanity\") {\n    for (compression <- CompressionType.values) {\n\n      for (truncate <- Seq(false, true)) {\n        println(s\"testing datasource with compression=$compression, truncate=$truncate\")\n        df.write\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME)\n          // toLowerCase to test case insensitivity\n          .option(SinglestoreOptions.LOAD_DATA_COMPRESSION, compression.toString.toLowerCase())\n          .option(SinglestoreOptions.TRUNCATE, truncate.toString.toLowerCase())\n          .mode(SaveMode.Overwrite)\n          .save(\"testdb.tb2\")\n\n        val x = spark.read\n          .format(\"jdbc\")\n          .option(\"url\", s\"jdbc:mysql://$masterHost:$masterPort/testdb\")\n          .option(\"dbtable\", \"testdb.tb2\")\n          .option(\"user\", \"root\")\n          .option(\"password\", masterPassword)\n          .load()\n\n        assertSmallDataFrameEquality(x, df, true, true, orderedComparison = false)\n      }\n    }\n  }\n\n  describe(\"DataSource V1 can create table with different kinds of keys\") {\n    def createTableWithKeys(keys: Map[String, String]) =\n      df.write\n        .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n        .mode(SaveMode.Overwrite)\n        .options(keys)\n        .save(\"testdb.keytest\")\n\n    it(\"works for all key types\") {\n      for (keyType <- TableKeyType.values) {\n        if (keyType != TableKeyType.Shard) {\n          println(s\"testing create table with keyType=$keyType\")\n          createTableWithKeys(\n            Map(\n              s\"tableKey.shard\"               -> \"name\",\n              s\"tableKey.${keyType.toString}\" -> \"name\"\n            ))\n        }\n      }\n    }\n\n    it(\"errors when the user specifies an invalid key type\") {\n      assertThrows[RuntimeException] {\n        createTableWithKeys(Map(s\"tableKey.foo.id\" -> \"id\"))\n      }\n    }\n\n    it(\"errors when the user specifies duplicate key names\") {\n      assertThrows[SQLSyntaxErrorException] {\n        createTableWithKeys(\n          Map(\n            s\"tableKey.key.foo\"    -> \"id\",\n            s\"tableKey.unique.foo\" -> \"id\"\n          )\n        )\n      }\n    }\n\n    it(\"throws when no type and no name is specified\") {\n      assertThrows[RuntimeException] {\n        // no type specified, no name specified\n        createTableWithKeys(Map(s\"tableKey..\" -> \"id\"))\n      }\n    }\n\n    it(\"throws when no type is specified\") {\n      assertThrows[RuntimeException] {\n        // no type specified\n        createTableWithKeys(Map(s\"tableKey.\" -> \"id\"))\n      }\n    }\n\n    it(\"throws when no name is specified\") {\n      assertThrows[RuntimeException] {\n        // no type specified\n        createTableWithKeys(Map(s\"tableKey.key.\" -> \"id\"))\n      }\n    }\n\n    it(\"supports multiple columns\") {\n      createTableWithKeys(Map(s\"tableKey.primary\"     -> \"id, name\"))\n      createTableWithKeys(Map(s\"tableKey.columnstore\" -> \"id, name\"))\n      createTableWithKeys(Map(s\"tableKey.key\"         -> \"id, name\"))\n      createTableWithKeys(\n        Map(\n          s\"tableKey.unique\" -> \"id, name\",\n          s\"tableKey.shard\"  -> \"id, name\"\n        ))\n    }\n\n    it(\"supports spaces and dots in the key name\") {\n      createTableWithKeys(Map(s\"tableKey.primary.foo.bar\" -> \"id\"))\n      createTableWithKeys(Map(s\"tableKey.key.cool key\"    -> \"id\"))\n      createTableWithKeys(Map(s\"tableKey.key....\"         -> \"id\"))\n    }\n  }\n\n  def repartitionColumnsTest(): Unit = {\n    val expectedDf = spark.createDF(\n      List((1, 1, \"Albert\", \"1\"), (5, 2, \"Ronny\", \"2\"), (7, 3, \"Ben\", \"4\"), (9, 4, \"David\", \"5\")),\n      List((\"id\", IntegerType, true),\n           (\"id2\", IntegerType, true),\n           (\",a,,\", StringType, true),\n           (\" \", StringType, true))\n    )\n    writeTable(\"testdb.foo\", expectedDf)\n\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.enableParallelRead\", \"forced\")\n    val actualDf = spark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"parallelRead.repartition\", \"true\")\n      .option(\"parallelRead.repartition.columns\", \"id,   `,a,,` ,   ` `\")\n      .load(\"testdb.foo\")\n\n    assertSmallDataFrameEquality(actualDf, expectedDf, orderedComparison = false)\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.enableParallelRead\", \"automaticLite\")\n  }\n\n  it(\"repartition columns pushdown enabled\") {\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.disablePushdown\", \"false\")\n    repartitionColumnsTest()\n  }\n\n  it(\"repartition columns pushdown disabled\") {\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.disablePushdown\", \"true\")\n    repartitionColumnsTest()\n    spark.sqlContext.setConf(\"spark.datasource.singlestore.disablePushdown\", \"false\")\n  }\n\n  it(\"creates rowstore table\") {\n    df.write\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"createRowstoreTable\", \"true\")\n      .save(\"testdb.rowstore\")\n\n    val rows = spark.executeSinglestoreQueryDB(\n      \"testdb\",\n      \"select storage_type from information_schema.tables where table_schema='testdb' and table_name='rowstore';\")\n    assert(rows.size == 1, \"Only one row should be selected\")\n    rows.foreach(row =>\n      assert(row.getString(0).equals(\"INMEMORY_ROWSTORE\"), \"Should create rowstore table\"))\n  }\n\n  describe(\"parallel read num partitions\") {\n\n    def testNumPartitions(feature: String, numPartitions: Int, expectedNumPartitions: Int): Unit = {\n      val initialConf = spark.conf.getAll\n      spark.conf.set(\"spark.datasource.singlestore.parallelRead.Features\", feature)\n      spark.conf.set(\"spark.datasource.singlestore.enableParallelRead\", \"forced\")\n\n      try {\n        val df = spark.read\n          .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n          .option(\"enableParallelRead\", \"forced\")\n          .option(\"parallelRead.maxNumPartitions\", numPartitions.toString)\n          .load(\"testdb.foo\")\n          .cache()\n\n        assert(df.rdd.getNumPartitions == expectedNumPartitions,\n               \"Wrong number of partition in the resulting RDD\")\n      } finally {\n        val finalConf = spark.conf.getAll\n        finalConf.foreach(c =>\n          if (c._1.startsWith(\"spark.datasource.singlestore\")) {\n            spark.conf.unset(c._1)\n        })\n        initialConf.foreach(c =>\n          if (c._1.startsWith(\"spark.datasource.singlestore\")) {\n            spark.conf.set(c._1, c._2)\n        })\n      }\n    }\n\n    for (feature <- Seq(\"readFromLeaves\", \"readFromAggregators\", \"readFromAggregatorsMaterialized\")) {\n      if (canDoParallelReadFromAggregators || feature == \"readFromLeaves\") {\n        describe(feature) {\n          it(\"default\") {\n            testNumPartitions(feature, 0, 2)\n          }\n          it(\"single partition\") {\n            testNumPartitions(feature, 1, 1)\n          }\n          it(\"a lot of partitions\") {\n            testNumPartitions(feature, 5, 2)\n          }\n        }\n      }\n    }\n  }\n\n  it(\"JWT authentication\") {\n    val jwtSpark = SparkSession\n      .builder()\n      .master(\"local\")\n      .appName(\"singlestore-integration-jwt-test\")\n      .config(\n        \"spark.datasource.singlestore.ddlEndpoint\",\n        s\"${masterHost}:${masterPort}\"\n      )\n      .config(\"spark.datasource.singlestore.user\", \"test_jwt_user\")\n      .config(\n        \"spark.datasource.singlestore.password\",\n        masterJWTPassword\n      )\n      .config(\"spark.datasource.singlestore.database\", \"testdb\")\n      .config(\"spark.datasource.singlestore.credentialType\", \"JWT\")\n      .getOrCreate()\n\n    // Read with enabled sslMode\n    val jwtDF = jwtSpark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(\"sslMode\", \"trust\")\n      .option(SinglestoreOptions.TABLE_NAME, \"testdb.foo\")\n      .load()\n    assertSmallDataFrameEquality(jwtDF, df, orderedComparison = false)\n  }\n\n  it(\"clientEndpoint option\") {\n    val cloudSpark = spark.newSession()\n\n    cloudSpark.conf.unset(\"spark.datasource.singlestore.ddlEndpoint\")\n    cloudSpark.conf.unset(\"spark.datasource.singlestore.dmlEndpoint\")\n    cloudSpark.conf.set(\"spark.datasource.singlestore.clientEndpoint\",\n                        s\"${masterHost}:${masterPort}\")\n\n    val jwtDF = cloudSpark.read\n      .format(DefaultSource.SINGLESTORE_SOURCE_NAME_SHORT)\n      .option(SinglestoreOptions.TABLE_NAME, \"testdb.foo\")\n      .load()\n    assertSmallDataFrameEquality(jwtDF, df, orderedComparison = false)\n  }\n\n  it(\"connection attributes\") {\n    assume(version.atLeast(\"8.1.0\"))\n\n    val conn = SinglestoreConnectionPool.getConnection(\n      JdbcHelpers.getDDLConnProperties(\n        new SinglestoreOptions(\n          s\"$masterHost:$masterPort\",\n          List.empty[String],\n          \"root\",\n          masterPassword,\n          None,\n          Map.empty[String, String],\n          false,\n          false,\n          Automatic,\n          List(ReadFromLeaves),\n          0,\n          0,\n          0,\n          0,\n          true,\n          Set.empty,\n          Truncate,\n          SinglestoreOptions.CompressionType.GZip,\n          SinglestoreOptions.LoadDataFormat.CSV,\n          List.empty[SinglestoreOptions.TableKey],\n          None,\n          10,\n          10,\n          false,\n          SinglestoreConnectionPoolOptions(enabled = true, -1, 8, 30000, 1000, -1, -1),\n          SinglestoreConnectionPoolOptions(enabled = true, -1, 8, 2000, 1000, -1, -1),\n          spark.sparkContext.version,\n          Option.empty\n        ),\n        false\n      )\n    )\n\n    val expectedAttributes = Map[String, String](\n      \"_connector_name\"    -> \"SingleStore Spark Connector\",\n      \"_connector_version\" -> BuildInfo.version,\n      \"_product_version\"   -> spark.sparkContext.version\n    )\n\n    var actualAttributes = Map[String, String]()\n    try {\n      val stmt = conn.createStatement()\n      try {\n        val rs = stmt.executeQuery(\n          \"select * from information_schema.mv_connection_attributes where CONNECTION_ID=CONNECTION_ID()\")\n        try {\n          while (rs.next()) {\n            actualAttributes = actualAttributes + (rs.getString(3) -> rs.getString(4))\n          }\n        } finally {\n          rs.close()\n        }\n      } finally {\n        stmt.close()\n      }\n\n      assert(expectedAttributes.toSet.subsetOf(actualAttributes.toSet))\n    } finally {\n      conn.close()\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/SinglestoreConnectionPoolTest.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.Connection\nimport java.time.Duration\nimport java.util.Properties\n\nimport org.apache.commons.dbcp2.DelegatingConnection\nimport org.apache.spark.sql.catalyst.util.CaseInsensitiveMap\n\nclass SinglestoreConnectionPoolTest extends IntegrationSuiteBase {\n  var properties = new Properties()\n\n  override def beforeEach(): Unit = {\n    super.beforeEach()\n    properties = JdbcHelpers.getConnProperties(\n      SinglestoreOptions(\n        CaseInsensitiveMap(\n          Map(\"ddlEndpoint\" -> s\"$masterHost:$masterPort\", \"password\" -> masterPassword)),\n          spark.sparkContext),\n      isOnExecutor = false,\n      s\"$masterHost:$masterPort\"\n    )\n  }\n\n  override def afterEach(): Unit = {\n    super.afterEach()\n    SinglestoreConnectionPool.close()\n  }\n\n  it(\"reuses a connection\") {\n    var conn  = SinglestoreConnectionPool.getConnection(properties)\n    val conn1 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    conn = SinglestoreConnectionPool.getConnection(properties)\n    val conn2 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    assert(conn1 == conn2, \"should reuse idle connection\")\n  }\n\n  it(\"creates a new connection when existing is in use\") {\n    val conn1 = SinglestoreConnectionPool.getConnection(properties)\n    val conn2 = SinglestoreConnectionPool.getConnection(properties)\n    val originalConn1 =\n      conn1.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    val originalConn2 =\n      conn2.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n\n    assert(originalConn1 != originalConn2, \"should create a new connection when existing is in use\")\n\n    conn1.close()\n    conn2.close()\n  }\n\n  it(\"creates different pools for different properties\") {\n    var conn  = SinglestoreConnectionPool.getConnection(properties)\n    val conn1 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    properties.setProperty(\"newProperty\", \"\")\n\n    conn = SinglestoreConnectionPool.getConnection(properties)\n    val conn2 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    assert(conn1 != conn2, \"should create different pools for different properties\")\n  }\n\n  it(\"maxTotal and maxWaitMillis\") {\n    val maxWaitMillis = 200\n    properties.setProperty(\"maxTotal\", \"1\")\n    properties.setProperty(\"maxWaitMillis\", maxWaitMillis.toString)\n\n    val conn1 = SinglestoreConnectionPool.getConnection(properties)\n\n    val start = System.nanoTime()\n    try {\n      SinglestoreConnectionPool.getConnection(properties)\n      fail()\n    } catch {\n      case e: Throwable =>\n        assert(\n          e.getMessage.equals(\n            \"Cannot get a connection, pool error Timeout waiting for idle object\"),\n          \"should throw timeout error\"\n        )\n        assert(System.nanoTime() - start > Duration.ofMillis(maxWaitMillis).toNanos,\n               \"should throw timeout error after 1 sec\")\n    }\n\n    conn1.close()\n  }\n\n  it(\"eviction of idle connections\") {\n    val minEvictableIdleTimeMillis    = 100\n    val timeBetweenEvictionRunsMillis = 50\n    properties.setProperty(\"minEvictableIdleTimeMillis\", minEvictableIdleTimeMillis.toString)\n    properties.setProperty(\"timeBetweenEvictionRunsMillis\", timeBetweenEvictionRunsMillis.toString)\n\n    var conn  = SinglestoreConnectionPool.getConnection(properties)\n    val conn1 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    Thread.sleep(((minEvictableIdleTimeMillis + timeBetweenEvictionRunsMillis) * 1.1).toLong)\n\n    conn = SinglestoreConnectionPool.getConnection(properties)\n    val conn2 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    assert(conn1 != conn2, \"should evict idle connection\")\n  }\n\n  it(\"maxConnLifetimeMillis\") {\n    val maxConnLifetimeMillis = 100\n    properties.setProperty(\"maxConnLifetimeMillis\", maxConnLifetimeMillis.toString)\n    var conn  = SinglestoreConnectionPool.getConnection(properties)\n    val conn1 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    Thread.sleep((maxConnLifetimeMillis * 1.1).toLong)\n\n    conn = SinglestoreConnectionPool.getConnection(properties)\n    val conn2 = conn.asInstanceOf[DelegatingConnection[Connection]].getInnermostDelegateInternal\n    conn.close()\n\n    assert(conn1 != conn2, \"should not use a connection after end of lifetime\")\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/SinglestoreOptionsTest.scala",
    "content": "package com.singlestore.spark\n\nimport org.apache.spark.sql.catalyst.util.CaseInsensitiveMap\n\nclass SinglestoreOptionsTest extends IntegrationSuiteBase {\n  val requiredOptions = Map(\"ddlEndpoint\" -> \"h:3306\")\n\n  describe(\"equality\") {\n    it(\"should sort dmlEndpoints\") {\n      assert(\n        SinglestoreOptions(\n          CaseInsensitiveMap(\n            requiredOptions ++ Map(\"dmlEndpoints\" -> \"host1:3302,host2:3302,host1:3342\")),\n          spark.sparkContext) ==\n          SinglestoreOptions(\n            CaseInsensitiveMap(\n              requiredOptions ++ Map(\"dmlEndpoints\" -> \"host2:3302,host1:3302,host1:3342\")),\n            spark.sparkContext),\n        \"Should sort dmlEndpoints\"\n      )\n    }\n  }\n\n  describe(\"splitEscapedColumns\") {\n    it(\"empty string\") {\n      assert(SinglestoreOptions.splitEscapedColumns(\"\") == List())\n    }\n\n    it(\"3 columns\") {\n      assert(\n        SinglestoreOptions.splitEscapedColumns(\"col1,col2,col3\") == List(\"col1\", \"col2\", \"col3\"))\n    }\n\n    it(\"with spaces\") {\n      assert(\n        SinglestoreOptions\n          .splitEscapedColumns(\"  col1 , col2,   col3\") == List(\"  col1 \", \" col2\", \"   col3\"))\n    }\n\n    it(\"with backticks\") {\n      assert(\n        SinglestoreOptions.splitEscapedColumns(\" ` col1` , `col2`, ``  col3\") == List(\" ` col1` \",\n                                                                                      \" `col2`\",\n                                                                                      \" ``  col3\"))\n    }\n\n    it(\"with commas inside of backticks\") {\n      assert(\n        SinglestoreOptions\n          .splitEscapedColumns(\" ` ,, col1,` , ``,```,col3`, ``  col4,`,,`\") == List(\n          \" ` ,, col1,` \",\n          \" ``\",\n          \"```,col3`\",\n          \" ``  col4\",\n          \"`,,`\"))\n    }\n  }\n\n  describe(\"trimAndUnescapeColumn\") {\n    it(\"empty string\") {\n      assert(SinglestoreOptions.trimAndUnescapeColumn(\"\") == \"\")\n    }\n\n    it(\"spaces\") {\n      assert(SinglestoreOptions.trimAndUnescapeColumn(\"   \") == \"\")\n    }\n\n    it(\"in backticks\") {\n      assert(SinglestoreOptions.trimAndUnescapeColumn(\" `asd`  \") == \"asd\")\n    }\n\n    it(\"backticks in the result\") {\n      assert(SinglestoreOptions.trimAndUnescapeColumn(\" ```a``sd`  \") == \"`a`sd\")\n    }\n\n    it(\"several escaped words\") {\n      assert(SinglestoreOptions.trimAndUnescapeColumn(\" ```a``sd` ```a``sd` \") == \"`a`sd `a`sd\")\n    }\n\n    it(\"backtick in the middle of string\") {\n      assert(\n        SinglestoreOptions\n          .trimAndUnescapeColumn(\" a```a``sd` ```a``sd` \") == \"a```a``sd` ```a``sd`\")\n    }\n  }\n\n  it(\"disablePushdown and customSchema\") {\n    assertThrows[IllegalArgumentException] {\n      SinglestoreOptions(CaseInsensitiveMap(requiredOptions ++ Map(\"customSchema\" -> \"a INT\")),\n        spark.sparkContext)\n    }\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/TestHelper.scala",
    "content": "package com.singlestore.spark\n\nimport java.sql.SQLException\n\nimport scala.annotation.tailrec\n\nobject TestHelper {\n\n  @tailrec\n  def isSQLExceptionWithCode(e: Throwable, codes: List[Integer]): Boolean = e match {\n    case e: SQLException if codes.contains(e.getErrorCode) => true\n    case e if e.getCause != null                           => isSQLExceptionWithCode(e.getCause, codes)\n    case e =>\n      e.printStackTrace()\n      false\n  }\n}\n"
  },
  {
    "path": "src/test/scala/com/singlestore/spark/VersionTest.scala",
    "content": "package com.singlestore.spark\n\nimport com.singlestore.spark.SQLGen.SinglestoreVersion\nimport org.scalatest.funspec.AnyFunSpec\n\nclass VersionTest extends AnyFunSpec {\n\n  it(\"singlestore version test\") {\n\n    assert(SinglestoreVersion(\"7.0.1\").atLeast(\"6.8.1\"))\n    assert(!SinglestoreVersion(\"6.8.1\").atLeast(\"7.0.1\"))\n    assert(SinglestoreVersion(\"7.0.2\").atLeast(\"7.0.1\"))\n    assert(SinglestoreVersion(\"7.0.10\").atLeast(\"7.0.9\"))\n    assert(SinglestoreVersion(\"7.2.5\").atLeast(\"7.1.99999\"))\n    assert(SinglestoreVersion(\"7.2.500\").atLeast(\"7.2.499\"))\n  }\n}\n"
  }
]