├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── build.gradle ├── config └── checkstyle │ └── checkstyle.xml ├── dbConfig ├── dbSetupTrace ├── dbTrace ├── deploy.sh ├── deploy └── peloton-test.tar ├── documentation └── High Level Architecture Documentation.pdf ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── script ├── config.py ├── keyword_filter ├── test.py └── trace_files ├── setup.sql ├── src ├── main │ └── java │ │ └── edu │ │ └── cmu │ │ └── cs │ │ └── db │ │ └── peloton │ │ └── test │ │ ├── app │ │ ├── Args.java │ │ ├── JUnitRunner.java │ │ ├── Main.java │ │ └── TestInitialization.java │ │ ├── common │ │ ├── DatabaseDefinition.java │ │ ├── DatabaseWrapper.java │ │ └── TestContainer.java │ │ └── generate │ │ ├── ast │ │ ├── Ast.java │ │ ├── Context.java │ │ ├── ListElem.java │ │ ├── ProductElem.java │ │ └── SumElem.java │ │ ├── defn │ │ ├── Aggregate.java │ │ ├── FromClass.java │ │ ├── FromProp.java │ │ ├── Join.java │ │ ├── PropertySpec.java │ │ ├── SearchCondition.java │ │ ├── Select.java │ │ ├── SelectProp.java │ │ ├── Star.java │ │ └── operators │ │ │ ├── ConstantOption.java │ │ │ ├── SqlBinaryOperator.java │ │ │ ├── SqlOperator.java │ │ │ └── SqlUnaryOperator.java │ │ └── util │ │ ├── Iterators.java │ │ └── RandomUtils.java └── test │ └── java │ └── edu │ └── cmu │ └── cs │ └── db │ └── peloton │ └── test │ └── generate │ └── ast │ ├── ProductElemTest.java │ ├── StubElem.java │ ├── SumElemTest.java │ ├── TestListElem.java │ └── TestUtil.java ├── tags └── test_out └── foo /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff: 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/dictionaries 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | .gradle/* 24 | build/* 25 | 26 | # Mongo Explorer plugin: 27 | .idea/**/mongoSettings.xml 28 | 29 | ## File-based project format: 30 | *.iws 31 | 32 | ## Plugin-specific files: 33 | 34 | # IntelliJ 35 | /out/ 36 | 37 | # mpeltonen/sbt-idea plugin 38 | .idea_modules/ 39 | 40 | # JIRA plugin 41 | atlassian-ide-plugin.xml 42 | 43 | # Crashlytics plugin (for Android Studio and IntelliJ) 44 | com_crashlytics_export_strings.xml 45 | crashlytics.properties 46 | crashlytics-build.properties 47 | fabric.properties 48 | ### Java template 49 | # Compiled class file 50 | *.class 51 | 52 | # Log file 53 | *.log 54 | 55 | # BlueJ files 56 | *.ctxt 57 | 58 | # Mobile Tools for Java (J2ME) 59 | .mtj.tmp/ 60 | 61 | # Package Files # 62 | *.jar 63 | *.war 64 | *.ear 65 | *.zip 66 | *.tar.gz 67 | *.rar 68 | 69 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 70 | hs_err_pid* 71 | 72 | .idea/ 73 | test_reports/ 74 | !/gradle/wrapper/gradle-wrapper.jar 75 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | env: TERM=dumb 7 | 8 | script: timeout 600 gradle check 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # peloton-test 2 | Testing Framework for the Peloton DBMS 3 | 4 | You can find more information about the project in the documentation folder. 5 | 6 | To run the command line tool for query generation, execute the following command, if output directory is provided, 7 | XML output is written to that directory. Otherwise the result is printed to the command line. 8 | ``` 9 | ./run.sh -config -trace [-out ] [-batchsize ] 10 | ``` 11 | 12 | Currently only postgresql is supported 13 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'eclipse' 3 | apply plugin: 'checkstyle' 4 | apply plugin: 'jacoco' 5 | apply plugin: 'findbugs' 6 | apply plugin: 'application' 7 | 8 | test.testLogging { exceptionFormat "full"; events "failed", "passed", "skipped" } 9 | 10 | configurations.all { 11 | resolutionStrategy { 12 | force 'org.ow2.asm:asm:5.0.3' 13 | forcedModules = [ 'org.ow2.asm:asm:5.0.3' ] 14 | } 15 | } 16 | 17 | mainClassName = "edu.cmu.cs.db.peloton.test.app.Main" 18 | 19 | run { 20 | if (project.hasProperty("args")) { 21 | args project.args.split('\\s') 22 | } 23 | } 24 | 25 | repositories { 26 | mavenCentral() 27 | } 28 | 29 | 30 | dependencies { 31 | compile group: 'junit', name: 'junit', version: '4.12' 32 | compile group: 'com.google.guava', name: 'guava', version: '21.0' 33 | // https://mvnrepository.com/artifact/org.postgresql/postgresql 34 | compile group: 'org.postgresql', name: 'postgresql', version: '9.4-1206-jdbc4' 35 | compile group: 'org.apache.ant', name: 'ant-junit', version: '1.10.1' 36 | compile group: 'com.beust', name: 'jcommander', version: '1.7' 37 | } 38 | 39 | dependencies { 40 | runtime fileTree(dir: 'lib', include: '*.jar') 41 | } 42 | 43 | checkstyle { 44 | ignoreFailures = true 45 | toolVersion = "6.7" 46 | sourceSets = [sourceSets.main] 47 | } 48 | 49 | findbugs { 50 | ignoreFailures = true 51 | } 52 | 53 | tasks.withType(FindBugs) { 54 | reports { 55 | xml.enabled false 56 | html.enabled true 57 | } 58 | } 59 | 60 | buildscript{ 61 | repositories { 62 | mavenCentral() 63 | } 64 | 65 | } 66 | 67 | jacocoTestReport { 68 | reports { 69 | xml.enabled false 70 | csv.enabled false 71 | html.destination "${buildDir}/reports/coverage" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /config/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /dbConfig: -------------------------------------------------------------------------------- 1 | jdbc:postgresql://127.0.0.1:5432/test1 2 | jenkins 3 | jenkinsTest123 4 | jdbc:postgresql://127.0.0.1:5432/test2 5 | jenkins 6 | jenkinsTest123 -------------------------------------------------------------------------------- /dbSetupTrace: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS foobar; 2 | CREATE TABLE foobar( field1 int, field2 int, field3 int); 3 | INSERT INTO foobar VALUES (12, 30, 45), (10, 20, 25), (15, 40, 35); -------------------------------------------------------------------------------- /dbTrace: -------------------------------------------------------------------------------- 1 | SELECT * FROM foo 2 | SELECT * FROM bar 3 | SELECT * FROM foo 4 | SELECT * FROM foobar; -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ./gradlew build 3 | cp build/distributions/peloton-test.tar deploy/ 4 | -------------------------------------------------------------------------------- /deploy/peloton-test.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmu-db/peloton-test/9da1ccf9b7c6971ba040004164b0a0929a850830/deploy/peloton-test.tar -------------------------------------------------------------------------------- /documentation/High Level Architecture Documentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmu-db/peloton-test/9da1ccf9b7c6971ba040004164b0a0929a850830/documentation/High Level Architecture Documentation.pdf -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmu-db/peloton-test/9da1ccf9b7c6971ba040004164b0a0929a850830/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Apr 16 16:45:19 EDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save ( ) { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /script/config.py: -------------------------------------------------------------------------------- 1 | # parameters for connecting to postgresql 2 | pg_database = "SQLite_trace_test" 3 | pg_username = "jenkins" 4 | pg_password = "jenkinsTest123" 5 | 6 | # parameters for connecting to postgresql 7 | peloton_path = "../../build/bin/peloton" 8 | peloton_port = 52726 9 | peloton_username = "" 10 | peloton_password = "" 11 | 12 | # sqllite trace file path 13 | trace_file_root = "trace" 14 | 15 | # peloton-test path 16 | peloton_test_path = "../" 17 | 18 | # log file 19 | peloton_log_file = "peloton_log" 20 | peloton_test_log_file = "test_log" 21 | 22 | # trace files 23 | traces = map(lambda x: x.strip(), open("trace_files").readlines()) 24 | 25 | # sql keyword filter 26 | # ** in upper case ** 27 | kw_filter = map(lambda x: x.strip(), open("keyword_filter").readlines()) 28 | -------------------------------------------------------------------------------- /script/keyword_filter: -------------------------------------------------------------------------------- 1 | CASE 2 | BETWEEN 3 | <> 4 | IS 5 | \(SELECT 6 | ABS 7 | -------------------------------------------------------------------------------- /script/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import os 4 | import re 5 | import sys 6 | import socket 7 | import time 8 | from config import * 9 | from subprocess import Popen 10 | 11 | tmp_dir = "tmp" 12 | tmp_sql_file = "tmp/test.sql" 13 | tmp_config = "tmp/config" 14 | 15 | def extract_sql(input_path, output_path): 16 | infile = open(input_path) 17 | outfile = open(output_path, "w") 18 | 19 | new_statement = True 20 | wait_for_blank = False 21 | query = "" 22 | 23 | for line in infile: 24 | line = line.strip() + " " 25 | if line == " ": 26 | if not wait_for_blank: 27 | outfile.write(query + ";\n") 28 | new_statement = True 29 | wait_for_blank = False 30 | query = "" 31 | continue 32 | if wait_for_blank: 33 | continue 34 | l = line.strip().split() 35 | if l[0] in ['#', 'statement', 'query', 'halt', 'hash-threshold']: 36 | continue 37 | if l[0] in ['onlyif', 'skipif']: 38 | wait_for_blank = True 39 | continue 40 | if l[0] == '----': 41 | outfile.write(query + ";\n") 42 | wait_for_blank = True 43 | continue 44 | query += line 45 | for kw in kw_filter: 46 | if re.search(kw, line.upper()): 47 | wait_for_blank = True 48 | 49 | infile.close() 50 | outfile.close() 51 | 52 | def get_pg_jdbc(): 53 | return "jdbc:postgresql://127.0.0.1:5432/%s" % pg_database 54 | 55 | def get_peloton_jdbc(): 56 | return "jdbc:postgresql://127.0.0.1:%s/" % peloton_port 57 | 58 | def gen_config(path): 59 | conf = open(path, "w") 60 | 61 | conf.write(get_peloton_jdbc() + "\n") 62 | conf.write(peloton_username + "\n") 63 | conf.write(peloton_password + "\n") 64 | 65 | conf.write(get_pg_jdbc() + "\n") 66 | conf.write(pg_username + "\n") 67 | conf.write(pg_password + "\n") 68 | 69 | def wait_for_peloton_ready(): 70 | peloton_ready = False 71 | while not peloton_ready: 72 | try: 73 | sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 74 | sk.settimeout(1) 75 | sk.connect(('127.0.0.1', peloton_port)) 76 | peloton_ready = True 77 | sk.close() 78 | except Exception: 79 | pass 80 | if not peloton_ready: 81 | time.sleep(1) 82 | 83 | def main(): 84 | global peloton_path, tmp_dir, tmp_config, tmp_sql_file 85 | 86 | work_path = os.path.abspath(".") 87 | tmp_config = os.path.abspath(tmp_config) 88 | tmp_sql_file = os.path.abspath(tmp_sql_file) 89 | test_log_file = os.path.abspath(peloton_test_log_file) 90 | 91 | if not os.path.exists(tmp_dir): 92 | os.mkdir(tmp_dir) 93 | gen_config(tmp_config) 94 | 95 | peloton_log = open(peloton_log_file, "w") 96 | test_log = open(test_log_file, "w") 97 | 98 | print_log = Popen(["tail", "-f", test_log_file]) 99 | 100 | try: 101 | for path in traces: 102 | extract_sql(path, tmp_sql_file) 103 | print "==============================================================" 104 | print "test: ", path 105 | print "==============================================================" 106 | sys.stdout.flush() 107 | 108 | os.system("dropdb --if-exist -U %s -w %s" % (pg_username, pg_database)) 109 | os.system("createdb -U %s -w %s" % (pg_username, pg_database)) 110 | 111 | peloton = Popen([peloton_path, "-port", str(peloton_port)], 112 | stdout=peloton_log, stderr=peloton_log) 113 | 114 | wait_for_peloton_ready() 115 | 116 | os.chdir(peloton_test_path) 117 | test = Popen(["bash", "run.sh", "-config", tmp_config, "-trace", tmp_sql_file, "-out", "test_out", "-batchsize", "100"], 118 | stdout=test_log, stderr=test_log) 119 | test.wait() 120 | os.chdir(work_path) 121 | peloton.kill() 122 | except Exception as e: 123 | print e 124 | finally: 125 | peloton.kill() 126 | 127 | print_log.kill() 128 | #os.system("rm -rf %s" % tmp_dir) 129 | os.system("dropdb --if-exist -U %s -w %s" % (pg_username, pg_database)) 130 | 131 | if __name__ == "__main__": 132 | main() 133 | -------------------------------------------------------------------------------- /script/trace_files: -------------------------------------------------------------------------------- 1 | sqllite/test/select1.test 2 | -------------------------------------------------------------------------------- /setup.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO bar VALUES (12, 30); 2 | INSERT INTO foo VALUES (12, 30); 3 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/app/Args.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.app; 2 | 3 | import com.beust.jcommander.Parameter; 4 | 5 | /** 6 | * Created by tianyuli on 4/14/17. 7 | */ 8 | public class Args { 9 | @Parameter(names = "-config", 10 | description = "path to configuration file specifying how to connect to test databases", 11 | required = true) 12 | private String configFile; 13 | 14 | @Parameter(names = "-trace", 15 | description = "path to trace file with one query per line to execute") 16 | private String traceFile; 17 | 18 | @Parameter(names = "-setupTrace", 19 | description = "path to a trace file with CREATE/INSERT/UPDATE queries to initialize table contents") 20 | private String setupTraceFile; 21 | 22 | @Parameter(names = "-out", 23 | description = "path to output dir, if empty then just print to command line") 24 | private String outDir; 25 | 26 | @Parameter(names = "-limit", description = "maximum amount of queries to be executed, only applies to generate") 27 | private int limit = 5000; 28 | 29 | @Parameter(names = "-batchsize", 30 | description = "maximum size of a batch to be tested") 31 | private int batchSize = 50; 32 | 33 | public String getConfigFile() { 34 | return configFile; 35 | } 36 | 37 | public String getTraceFile() { 38 | return traceFile; 39 | } 40 | 41 | public String getSetupTraceFile() { 42 | return setupTraceFile; 43 | } 44 | 45 | public String getOutDir() { 46 | return outDir; 47 | } 48 | 49 | public int getBatchSize() { 50 | return batchSize; 51 | } 52 | 53 | public int getLimit() { 54 | return limit; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/app/JUnitRunner.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.app; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.TestContainer; 4 | import junit.framework.Test; 5 | import junit.framework.TestResult; 6 | import org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter; 7 | import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest; 8 | import org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter; 9 | import org.junit.internal.TextListener; 10 | import org.junit.runner.Description; 11 | import org.junit.runner.JUnitCore; 12 | import org.junit.runner.Result; 13 | import org.junit.runner.notification.Failure; 14 | import org.junit.runner.notification.RunListener; 15 | 16 | import java.io.ByteArrayOutputStream; 17 | import java.io.File; 18 | import java.io.FileOutputStream; 19 | import java.io.PrintStream; 20 | 21 | /** 22 | * Created by tianyuli on 4/14/17. 23 | */ 24 | public class JUnitRunner { 25 | 26 | public void runTests(String outputDir) { 27 | JUnitCore junit = new JUnitCore(); 28 | if (outputDir == null) { 29 | junit.addListener(new TextListener(System.out)); 30 | } else { 31 | junit.addListener(new JUnitResultFormatterAsRunListener(new XMLJUnitResultFormatter()) { 32 | @Override 33 | public void testStarted(Description description) throws Exception { 34 | formatter.setOutput(new FileOutputStream( 35 | new File(outputDir, "TEST-" + description.getDisplayName() + ".xml"))); 36 | super.testStarted(description); 37 | } 38 | }); 39 | } 40 | junit.run(TestContainer.class); 41 | } 42 | 43 | /* Adopted from https://github.com/cloudbees/junit-standalone-runner/ */ 44 | 45 | /** 46 | * Wraps {@link Description} into {@link Test} enough to fake {@link JUnitResultFormatter}. 47 | */ 48 | public static class DescriptionAsTest implements Test { 49 | private final Description description; 50 | 51 | public DescriptionAsTest(Description description) { 52 | this.description = description; 53 | } 54 | 55 | public int countTestCases() { 56 | return 1; 57 | } 58 | 59 | public void run(TestResult result) { 60 | throw new UnsupportedOperationException(); 61 | } 62 | 63 | /** 64 | * {@link JUnitResultFormatter} determines the test name by reflection. 65 | */ 66 | public String getName() { 67 | return description.getDisplayName(); 68 | } 69 | 70 | @Override 71 | public boolean equals(Object o) { 72 | if (this == o) return true; 73 | if (o == null || getClass() != o.getClass()) return false; 74 | 75 | DescriptionAsTest that = (DescriptionAsTest) o; 76 | 77 | if (!description.equals(that.description)) return false; 78 | 79 | return true; 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return description.hashCode(); 85 | } 86 | } 87 | 88 | /** 89 | * Adopts {@link JUnitResultFormatter} into {@link RunListener}, 90 | * and also captures stdout/stderr by intercepting the likes of {@link System#out}. 91 | * 92 | * Because Ant JUnit formatter uses one stderr/stdout per one test suite, 93 | * we capture each test case into a separate report file. 94 | */ 95 | public static class JUnitResultFormatterAsRunListener extends RunListener { 96 | protected final JUnitResultFormatter formatter; 97 | private ByteArrayOutputStream stdout,stderr; 98 | private PrintStream oldStdout,oldStderr; 99 | private int problem; 100 | private long startTime; 101 | 102 | private JUnitResultFormatterAsRunListener(JUnitResultFormatter formatter) { 103 | this.formatter = formatter; 104 | } 105 | 106 | @Override 107 | public void testRunStarted(Description description) throws Exception { 108 | } 109 | 110 | @Override 111 | public void testRunFinished(Result result) throws Exception { 112 | } 113 | 114 | @Override 115 | public void testStarted(Description description) throws Exception { 116 | formatter.startTestSuite(new JUnitTest(description.getDisplayName())); 117 | formatter.startTest(new DescriptionAsTest(description)); 118 | problem = 0; 119 | startTime = System.currentTimeMillis(); 120 | 121 | this.oldStdout = System.out; 122 | this.oldStderr = System.err; 123 | System.setOut(new PrintStream(stdout = new ByteArrayOutputStream())); 124 | System.setErr(new PrintStream(stderr = new ByteArrayOutputStream())); 125 | } 126 | 127 | @Override 128 | public void testFinished(Description description) throws Exception { 129 | System.out.flush(); 130 | System.err.flush(); 131 | System.setOut(oldStdout); 132 | System.setErr(oldStderr); 133 | 134 | formatter.setSystemOutput(stdout.toString()); 135 | formatter.setSystemError(stderr.toString()); 136 | formatter.endTest(new DescriptionAsTest(description)); 137 | 138 | JUnitTest suite = new JUnitTest(description.getDisplayName()); 139 | suite.setCounts(1,problem,0); 140 | suite.setRunTime(System.currentTimeMillis()-startTime); 141 | formatter.endTestSuite(suite); 142 | } 143 | 144 | @Override 145 | public void testFailure(Failure failure) throws Exception { 146 | testAssumptionFailure(failure); 147 | } 148 | 149 | @Override 150 | public void testAssumptionFailure(Failure failure) { 151 | problem++; 152 | formatter.addError(new DescriptionAsTest(failure.getDescription()), failure.getException()); 153 | } 154 | 155 | @Override 156 | public void testIgnored(Description description) throws Exception { 157 | super.testIgnored(description); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/app/Main.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.app; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import edu.cmu.cs.db.peloton.test.common.DatabaseWrapper; 5 | import edu.cmu.cs.db.peloton.test.generate.defn.Select; 6 | import edu.cmu.cs.db.peloton.test.generate.util.Iterators; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.FileReader; 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Paths; 13 | import java.sql.*; 14 | import java.util.Collections; 15 | import java.util.Iterator; 16 | import java.util.Random; 17 | 18 | /** 19 | * Created by tianyuli on 3/20/17. 20 | */ 21 | public class Main { 22 | public static DatabaseWrapper testDb; 23 | public static DatabaseWrapper truthDb; 24 | public static Iterator queryProvider; 25 | public static int batchSize; 26 | 27 | /** 28 | * The entry point of application. 29 | * 30 | * @param args the input arguments 31 | * @throws SQLException the sql exception 32 | */ 33 | public static void main(String... args) throws SQLException, IOException{ 34 | Args parsedArgs = new Args(); 35 | new JCommander(parsedArgs, args); 36 | 37 | try (BufferedReader config = new BufferedReader(new FileReader(parsedArgs.getConfigFile()))) { 38 | testDb = new DatabaseWrapper(config.readLine(), config.readLine(), config.readLine()); 39 | truthDb = new DatabaseWrapper(config.readLine(), config.readLine(), config.readLine()); 40 | } 41 | 42 | TestInitialization.runSetupTraceIfExists(parsedArgs.getSetupTraceFile()); 43 | 44 | batchSize = parsedArgs.getBatchSize(); 45 | queryProvider = parsedArgs.getTraceFile() == null 46 | ? Iterators.fromAst(new Select(), parsedArgs.getLimit(),truthDb.getDatabaseDefinition(), new Random()) 47 | : Files.lines(Paths.get(parsedArgs.getTraceFile())).iterator(); 48 | 49 | while (queryProvider.hasNext()) { 50 | new JUnitRunner().runTests(parsedArgs.getOutDir()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/app/TestInitialization.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.app; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.FileNotFoundException; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.sql.SQLException; 8 | 9 | /** 10 | * Created by tianyuli on 5/7/17. 11 | */ 12 | public class TestInitialization { 13 | 14 | /** 15 | * Given a setup/initialization trace file via the 'setup_trace' optional argument 16 | * this function runs all DDL/Insert/Update/Delete statements to build the table. 17 | * Alternatively, an option to randomly create/populate tables will be added in 18 | * future versions. Any 'SELECT' statements will be ignored. 19 | * @param setupTraceFile 20 | * @throws SQLException 21 | */ 22 | public static void runSetupTraceIfExists(String setupTraceFile) 23 | throws SQLException, IOException { 24 | if(setupTraceFile == null) return; 25 | try (BufferedReader setupTrace = new BufferedReader(new FileReader(setupTraceFile))) { 26 | String query; 27 | while ((query = setupTrace.readLine()) != null) { 28 | if (isValidQuery(query)) { 29 | Main.testDb.getConnection().createStatement().executeUpdate(query); 30 | Main.truthDb.getConnection().createStatement().executeUpdate(query); 31 | } 32 | } 33 | } 34 | } 35 | 36 | public static boolean isValidQuery(String query) { 37 | return query.length() != 0 && !query.toUpperCase().startsWith("SELECT"); 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/common/DatabaseDefinition.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.common; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | 5 | import java.sql.JDBCType; 6 | import java.sql.SQLType; 7 | import java.util.Map; 8 | import java.util.stream.Stream; 9 | 10 | /** 11 | * Created by Tianyu on 3/26/17. 12 | */ 13 | public final class DatabaseDefinition { 14 | private final Map> tables; 15 | 16 | public static final class ColumnInfo { 17 | private final JDBCType type; 18 | 19 | ColumnInfo(JDBCType type) { 20 | this.type = type; 21 | } 22 | 23 | public JDBCType getType() { 24 | return type; 25 | } 26 | 27 | } 28 | 29 | private DatabaseDefinition(Map> tables) { 30 | this.tables = tables; 31 | } 32 | 33 | /** 34 | * A Builder object for DatabaseDefinition 35 | */ 36 | public static class Builder { 37 | private ImmutableMap.Builder> tableBuilder = ImmutableMap.builder(); 38 | private ImmutableMap.Builder columnsBuilder; 39 | private String currTable; 40 | 41 | /** 42 | * Instantiates a new Builder. 43 | */ 44 | public Builder() { 45 | tableBuilder = ImmutableMap.builder(); 46 | } 47 | 48 | /** 49 | * Start definition for a new table in the database. If a table is 50 | * currently being defined, that table definition is finalized 51 | * 52 | * @param name the name of the table 53 | * @return self reference for method chaining 54 | */ 55 | public DatabaseDefinition.Builder table(String name) { 56 | if (currTable != null) { 57 | tableBuilder.put(currTable, columnsBuilder.build()); 58 | } 59 | currTable = name; 60 | columnsBuilder = ImmutableMap.builder(); 61 | return this; 62 | } 63 | 64 | /** 65 | * Defines a column for the current table being defined. 66 | * 67 | * @param name the name of the column 68 | * @param type the type of values stored in the column 69 | * @return self reference for method chaining 70 | * @throws IllegalStateException if no table is being defined 71 | */ 72 | public DatabaseDefinition.Builder column(String name, JDBCType type) { 73 | if (currTable == null) { 74 | throw new IllegalStateException("No table being defined"); 75 | } 76 | columnsBuilder.put(name, new ColumnInfo(type)); 77 | return this; 78 | } 79 | 80 | /** 81 | * Builds database definition. 82 | * 83 | * @return the database definition 84 | */ 85 | public DatabaseDefinition build() { 86 | tableBuilder.put(currTable, columnsBuilder.build()); 87 | return new DatabaseDefinition(tableBuilder.build()); 88 | } 89 | } 90 | 91 | /** 92 | * Tables stream. 93 | * 94 | * @return the stream 95 | */ 96 | public Stream tables() { 97 | return tables.keySet().stream(); 98 | } 99 | 100 | public int tableCount() { 101 | return tables.size(); 102 | } 103 | 104 | /** 105 | * All columns stream. 106 | * 107 | * @return the stream 108 | */ 109 | public Stream> allColumns() { 110 | return tables.values().stream().flatMap(a -> a.entrySet().stream()); 111 | } 112 | 113 | /** 114 | * Gets table. 115 | * 116 | * @param tableName the table name 117 | * @return the table 118 | */ 119 | public Map getTable(String tableName) { 120 | return tables.get(tableName); 121 | } 122 | 123 | @Override 124 | public boolean equals(Object o) { 125 | if (this == o) return true; 126 | if (o == null || getClass() != o.getClass()) return false; 127 | 128 | DatabaseDefinition that = (DatabaseDefinition) o; 129 | 130 | return tables.equals(that.tables); 131 | } 132 | 133 | @Override 134 | public int hashCode() { 135 | return tables.hashCode(); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/common/DatabaseWrapper.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.common; 2 | 3 | import java.sql.*; 4 | import java.util.Map; 5 | 6 | /** 7 | * Provide access to a database for the application 8 | */ 9 | public class DatabaseWrapper implements AutoCloseable { 10 | private Connection conn; 11 | private DatabaseDefinition definition; 12 | private final String dbString, username, password; 13 | 14 | 15 | public DatabaseWrapper(String dbString, String username, String password) { 16 | conn = null; 17 | this.dbString = dbString; 18 | this.username = username; 19 | this.password = password; 20 | } 21 | 22 | /** 23 | * Gets connection. 24 | * 25 | * @return the connection to specified database. The connection is established the first 26 | * time this method is called 27 | * @throws SQLException the sql exception 28 | */ 29 | public Connection getConnection() throws SQLException { 30 | if (conn == null) { 31 | if (username.equals("")) { 32 | conn = DriverManager.getConnection(dbString); 33 | } else { 34 | conn = DriverManager.getConnection(dbString, username, password); 35 | } 36 | } 37 | return conn; 38 | } 39 | 40 | /** 41 | * Gets database definition. 42 | * 43 | * @return the database definition 44 | */ 45 | public DatabaseDefinition getDatabaseDefinition() { 46 | if (definition != null) { 47 | return definition; 48 | } 49 | 50 | DatabaseDefinition.Builder result = new DatabaseDefinition.Builder(); 51 | try { 52 | Connection conn = getConnection(); 53 | DatabaseMetaData metaData = conn.getMetaData(); 54 | String[] types = {"TABLE"}; 55 | ResultSet tables = metaData.getTables(null, null, "%", types); 56 | while (tables.next()) { 57 | String tableName = tables.getString("TABLE_NAME"); 58 | result.table(tableName); 59 | addTable(metaData, tableName, result); 60 | } 61 | definition = result.build(); 62 | return definition; 63 | } catch (SQLException e) { 64 | throw new RuntimeException(e); 65 | } 66 | } 67 | 68 | 69 | @Override 70 | public void close() throws Exception { 71 | if (conn != null) { 72 | conn.close(); 73 | } 74 | } 75 | 76 | private void addTable(DatabaseMetaData metaData, 77 | String table, 78 | DatabaseDefinition.Builder builder) throws SQLException { 79 | ResultSet columns = metaData.getColumns(null, null, table, null); 80 | while (columns.next()) { 81 | JDBCType type = JDBCType.valueOf(columns.getInt("DATA_TYPE")); 82 | builder.column(columns.getString("COLUMN_NAME"), type); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/common/TestContainer.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.common; 2 | 3 | import com.google.common.collect.Iterators; 4 | import edu.cmu.cs.db.peloton.test.app.Main; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.Parameterized; 8 | 9 | import java.math.BigDecimal; 10 | import java.sql.ResultSet; 11 | import java.sql.SQLException; 12 | import java.util.ArrayList; 13 | import java.util.HashSet; 14 | import java.util.List; 15 | import java.util.Set; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.runners.Parameterized.*; 19 | 20 | /** 21 | * Created by tianyuli on 4/3/17. 22 | */ 23 | @RunWith(Parameterized.class) 24 | public class TestContainer { 25 | 26 | @Parameters(name = "{index}: {0}") 27 | public static Iterable data() { 28 | List queries = new ArrayList<>(); 29 | for (int i = 0; i < Main.batchSize; i++) { 30 | if (!Main.queryProvider.hasNext()) { 31 | break; 32 | } 33 | queries.add(Main.queryProvider.next()); 34 | } 35 | return queries; 36 | } 37 | 38 | @Parameter 39 | public String query; 40 | 41 | @Test 42 | public void test() throws SQLException { 43 | if (query.toUpperCase().startsWith("SELECT")) { 44 | testEqual(Main.testDb.getConnection().createStatement().executeQuery(query), 45 | Main.truthDb.getConnection().createStatement().executeQuery(query)); 46 | } 47 | else { 48 | Main.testDb.getConnection().createStatement().executeUpdate(query); 49 | Main.truthDb.getConnection().createStatement().executeUpdate(query); 50 | } 51 | } 52 | 53 | private static void testEqual(ResultSet truth, ResultSet target) throws SQLException { 54 | Set> truthResults = new HashSet<>(); 55 | while (truth.next()) { 56 | truthResults.add(getRow(truth, truth.getMetaData().getColumnCount())); 57 | } 58 | 59 | Set> targetResults = new HashSet<>(); 60 | while (target.next()) { 61 | targetResults.add(getRow(target, target.getMetaData().getColumnCount())); 62 | } 63 | 64 | assertEquals(truthResults, targetResults); 65 | } 66 | 67 | private static List getRow(ResultSet resultSet, int columnCount) throws SQLException { 68 | List row = new ArrayList<>(); 69 | for (int i = 1; i <= columnCount; i++) { 70 | Object obj = resultSet.getObject(i); 71 | if (obj instanceof Double) { 72 | row.add(BigDecimal.valueOf((Double)obj)); 73 | } 74 | else if (obj instanceof Float) { 75 | row.add(BigDecimal.valueOf((Float)obj)); 76 | } 77 | else { 78 | row.add(obj); 79 | } 80 | } 81 | return row; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/ast/Ast.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import java.sql.JDBCType; 5 | import java.util.*; 6 | 7 | import static com.google.common.base.Preconditions.*; 8 | 9 | /** 10 | * Static Utility class that groups together various definitions in the 11 | * sql syntax tree implementation 12 | */ 13 | public final class Ast { 14 | /** 15 | * An Ast Clause consists of a sql string and the context this clause is in 16 | */ 17 | public static final class Clause { 18 | private final String clause; 19 | private final Context context; 20 | 21 | /** 22 | * Instantiates a new Clause. 23 | * 24 | * @param clause the clause string 25 | * @param context the context 26 | */ 27 | public Clause(String clause, Context context) { 28 | checkNotNull(clause); 29 | checkNotNull(context); 30 | this.clause = clause; 31 | this.context = context; 32 | } 33 | 34 | /** 35 | * Gets clause. 36 | * 37 | * @return the clause 38 | */ 39 | public String getClause() { 40 | return clause; 41 | } 42 | 43 | /** 44 | * Gets context. 45 | * 46 | * @return the context 47 | */ 48 | public Context getContext() { 49 | return context; 50 | } 51 | 52 | @Override 53 | public boolean equals(Object o) { 54 | if (this == o) return true; 55 | if (o == null || getClass() != o.getClass()) return false; 56 | 57 | Clause clause1 = (Clause) o; 58 | 59 | return clause.equals(clause1.clause) && context.equals(clause1.context); 60 | } 61 | 62 | @Override 63 | public int hashCode() { 64 | int result = clause.hashCode(); 65 | result = 31 * result + context.hashCode(); 66 | return result; 67 | } 68 | } 69 | 70 | /** 71 | * The interface that represents an element of the syntax tree. 72 | */ 73 | public interface Elem { 74 | /** 75 | * Randomly generates a value of this type using the given random generator. If the generation failed because 76 | * there is no value for the random walk down the tree, None is returned. 77 | * 78 | * @param db The database that queries will run on 79 | * @param context the context 80 | * @param random The random generator to be used 81 | * @return an Optional clause of this elem 82 | */ 83 | Optional generate(DatabaseDefinition db, Context context, Random random); 84 | 85 | default Clause generateUntilPresent(DatabaseDefinition db, Context context, Random random) { 86 | while (true) { 87 | Optional generated = generate(db, context, random); 88 | if (generated.isPresent()) { 89 | return generated.get(); 90 | } 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * Type of values in the ast, used as a lookup key type in context 97 | */ 98 | public interface Type { 99 | // Marker interface 100 | } 101 | 102 | public enum ExpressionType implements Type { 103 | NUMERIC, VARCHAR, BOOLEAN, ANY; 104 | 105 | public static ExpressionType fromJDBCType(JDBCType type) { 106 | // TODO research SQL types 107 | switch (type) { 108 | case DOUBLE: 109 | case FLOAT: 110 | case INTEGER: 111 | case NUMERIC: 112 | case DECIMAL: 113 | return NUMERIC; 114 | case VARCHAR: 115 | case LONGNVARCHAR: 116 | return VARCHAR; 117 | case BOOLEAN: 118 | return BOOLEAN; 119 | default: 120 | throw new IllegalArgumentException("Unimplemented"); 121 | } 122 | } 123 | 124 | public boolean matches(ExpressionType type) { 125 | if (type == ANY) { 126 | return true; 127 | } else { 128 | return equals(type); 129 | } 130 | } 131 | } 132 | 133 | /** 134 | * Sql language constructs 135 | */ 136 | public enum Sort implements Type { 137 | TABLE, COLUMN 138 | } 139 | 140 | 141 | private Ast() { 142 | // Should not instantiate 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/ast/Context.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import com.google.common.collect.Sets; 5 | 6 | import java.util.*; 7 | import java.util.stream.Stream; 8 | 9 | // TODO change to mutable if performance becomes an issue 10 | 11 | /** 12 | * Context for SQL clauses 13 | */ 14 | public final class Context { 15 | private final Map> values; 16 | 17 | /** 18 | * The constant EMPTY. 19 | */ 20 | public static final Context EMPTY = new Context(Collections.emptyMap()); 21 | 22 | private Context(Map> values) { 23 | this.values = values; 24 | } 25 | 26 | /** 27 | * Union context. 28 | * 29 | * @param other the other 30 | * @return the context 31 | */ 32 | public Context union(Context other) { 33 | return new Context(Stream.concat(values.entrySet().stream(), other.values.entrySet().stream()) 34 | .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue, Sets::union))); 35 | } 36 | 37 | /** 38 | * Add to scope context. Convenient method for when there is only one value to be added. 39 | * Otherwise, use a builder for better performance. 40 | * 41 | * @param type the type 42 | * @param value the value 43 | * @return the context 44 | */ 45 | public Context addToScope(Ast.Type type, String value) { 46 | return new Builder().putAll(this).put(type, value).build(); 47 | } 48 | 49 | /** 50 | * Gets all values currently in scope for a given type 51 | * 52 | * @param type the type of interest 53 | * @return all string values in scope of that type 54 | */ 55 | public Set valuesOf(Ast.Type type) { 56 | return values.getOrDefault(type, Collections.emptySet()); 57 | } 58 | 59 | /** 60 | * The type Builder. 61 | */ 62 | // TODO: fix redundant copying 63 | // Currently the copying happens twice when we construct a new context out of existing ones. 64 | // We should only need one. 65 | public static class Builder { 66 | private Map> builder = new HashMap<>(); 67 | 68 | /** 69 | * Put value into builder. 70 | * 71 | * @param type the type 72 | * @param value the value 73 | * @return the builder 74 | */ 75 | public Builder put(Ast.Type type, String value) { 76 | builder.computeIfAbsent(type, k -> new HashSet<>()).add(value); 77 | return this; 78 | } 79 | 80 | /** 81 | * Put all values into builder. 82 | * 83 | * @param context the context 84 | * @return the builder 85 | */ 86 | public Builder putAll(Context context) { 87 | builder.putAll(context.values); 88 | return this; 89 | } 90 | 91 | /** 92 | * Build context. 93 | * 94 | * @return the context 95 | */ 96 | public Context build() { 97 | return new Context(ImmutableMap.copyOf(builder)); 98 | } 99 | } 100 | 101 | @Override 102 | public boolean equals(Object o) { 103 | if (this == o) return true; 104 | if (o == null || getClass() != o.getClass()) return false; 105 | 106 | Context context = (Context) o; 107 | 108 | return values.equals(context.values); 109 | } 110 | 111 | @Override 112 | public int hashCode() { 113 | return values.hashCode(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/ast/ListElem.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.util.RandomUtils; 5 | 6 | import java.util.*; 7 | 8 | /** 9 | * Created by tianyuli on 4/1/17. 10 | */ 11 | public class ListElem implements Ast.Elem { 12 | private final Ast.Elem elem; 13 | private final int limit; 14 | private final List delimiters; 15 | 16 | public ListElem(Ast.Elem elem, int limit, String... delimiters) { 17 | this.elem = elem; 18 | this.limit = limit; 19 | this.delimiters = Arrays.asList(delimiters); 20 | } 21 | 22 | @Override 23 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 24 | int length = random.nextInt(limit) + 1; 25 | Set result = new HashSet<>(); 26 | for (int i = 0; i < length; i++) { 27 | Optional sub = elem.generate(db, context, random); 28 | if (sub.isPresent()) { 29 | result.add(sub.get()); 30 | } else { 31 | return Optional.empty(); 32 | } 33 | } 34 | return Optional.of(formatList(new ArrayList<>(result), delimiters, random)); 35 | 36 | } 37 | 38 | private static Ast.Clause formatList(List elems, List delimiters, Random random) { 39 | StringBuilder builder = new StringBuilder(); 40 | Context context = Context.EMPTY; 41 | for (int i = 0; i < elems.size(); i++) { 42 | Ast.Clause clause = elems.get(i); 43 | builder.append(clause.getClause()); 44 | if (i + 1 < elems.size()) { 45 | builder.append(RandomUtils.randomElement(delimiters, random)); 46 | } 47 | context = context.union(clause.getContext()); 48 | } 49 | return new Ast.Clause(builder.toString(), context); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/ast/ProductElem.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Optional; 8 | import java.util.Random; 9 | 10 | /** 11 | * Created by tianyuli on 4/1/17. 12 | */ 13 | public abstract class ProductElem implements Ast.Elem { 14 | protected abstract List args(); 15 | 16 | protected abstract String format(List args); 17 | 18 | @Override 19 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 20 | Context gamma = context; 21 | List result = new ArrayList<>(); 22 | for (Ast.Elem e : args()) { 23 | Optional clause = e.generate(db, gamma, random); 24 | if (!clause.isPresent()) { 25 | return Optional.empty(); 26 | } 27 | gamma = clause.get().getContext(); 28 | result.add(clause.get().getClause()); 29 | } 30 | return Optional.of(new Ast.Clause(format(result), gamma)); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/ast/SumElem.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.util.RandomUtils; 5 | 6 | import java.util.List; 7 | import java.util.Optional; 8 | import java.util.Random; 9 | 10 | /** 11 | * Created by tianyuli on 4/1/17. 12 | */ 13 | public abstract class SumElem implements Ast.Elem { 14 | protected abstract List args(); 15 | 16 | @Override 17 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 18 | return RandomUtils.randomElement(args(), random) 19 | .generate(db, context, random); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/Aggregate.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 6 | import edu.cmu.cs.db.peloton.test.generate.ast.SumElem; 7 | import edu.cmu.cs.db.peloton.test.generate.util.RandomUtils; 8 | 9 | import java.sql.SQLType; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.Optional; 13 | import java.util.Random; 14 | import java.util.function.Predicate; 15 | 16 | /** 17 | * Created by tianyuli on 4/2/17. 18 | */ 19 | public enum Aggregate implements Ast.Elem { 20 | ; 21 | private final String name; 22 | private final Predicate applicability; 23 | 24 | Aggregate(String name, Predicate applicability) { 25 | this.name = name; 26 | this.applicability = applicability; 27 | } 28 | 29 | @Override 30 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 31 | String table = RandomUtils.randomElement(context.valuesOf(Ast.Sort.TABLE), random); 32 | return null; 33 | 34 | } 35 | 36 | public static Ast.Elem all() { 37 | return new SumElem() { 38 | @Override 39 | protected List args() { 40 | return Arrays.asList(Aggregate.values()); 41 | } 42 | }; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/FromClass.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 6 | import edu.cmu.cs.db.peloton.test.generate.util.RandomUtils; 7 | 8 | import java.util.Optional; 9 | import java.util.Random; 10 | 11 | /** 12 | * Created by tianyuli on 4/1/17. 13 | */ 14 | public class FromClass implements Ast.Elem { 15 | 16 | @Override 17 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 18 | String table = RandomUtils.randomElement(db.tables(), db.tableCount(), random); 19 | return Optional.of(new Ast.Clause(table, context.addToScope(Ast.Sort.TABLE, table))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/FromProp.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.SumElem; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by tianyuli on 5/7/17. 11 | */ 12 | public class FromProp extends SumElem { 13 | @Override 14 | protected List args() { 15 | return ImmutableList.of(new Join(), new FromClass()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/Join.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import com.google.common.collect.Sets; 4 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 6 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 7 | import edu.cmu.cs.db.peloton.test.generate.defn.operators.SqlBinaryOperator; 8 | import edu.cmu.cs.db.peloton.test.generate.defn.operators.SqlUnaryOperator; 9 | import edu.cmu.cs.db.peloton.test.generate.util.RandomUtils; 10 | 11 | import java.util.*; 12 | 13 | /** 14 | * Created by tianyuli on 5/7/17. 15 | */ 16 | public class Join implements Ast.Elem { 17 | private static List joins = Arrays.asList("JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN"); 18 | 19 | @Override 20 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 21 | String leftTable = RandomUtils.randomElement(db.tables(), random); 22 | String rightTable = RandomUtils.randomElement(db.tables(), random); 23 | 24 | if (leftTable.equals(rightTable)) { 25 | return Optional.empty(); 26 | } 27 | 28 | Optional condition = new SearchCondition(Sets.union(EnumSet.allOf(SqlBinaryOperator.class), 29 | EnumSet.allOf(SqlUnaryOperator.class)), 2) 30 | .generate(db, context, random) 31 | .map(Ast.Clause::getClause); 32 | if (!condition.isPresent()) { 33 | return Optional.empty(); 34 | } 35 | return Optional.of(new Ast.Clause(formatJoin(random, leftTable, rightTable, condition.get()), 36 | context.addToScope(Ast.Sort.TABLE, leftTable).addToScope(Ast.Sort.TABLE, rightTable))); 37 | } 38 | 39 | private String formatJoin(Random random, String left, String right, String condition) { 40 | return String.format("%s %s %s ON %s", left, RandomUtils.randomElement(joins, random), right, condition); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/PropertySpec.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 6 | import edu.cmu.cs.db.peloton.test.generate.util.RandomUtils; 7 | 8 | import java.util.Optional; 9 | import java.util.Random; 10 | 11 | /** 12 | * Created by tianyuli on 4/1/17. 13 | */ 14 | public class PropertySpec implements Ast.Elem { 15 | 16 | @Override 17 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 18 | String table = RandomUtils.randomElement(context.valuesOf(Ast.Sort.TABLE), random); 19 | String column = String.format("%s.%s", table, 20 | RandomUtils.randomElement(db.getTable(table).entrySet(), random).getKey()); 21 | return Optional.of(new Ast.Clause(column, context.addToScope(Ast.Sort.COLUMN, column))); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/SearchCondition.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 6 | import edu.cmu.cs.db.peloton.test.generate.defn.operators.ConstantOption; 7 | import edu.cmu.cs.db.peloton.test.generate.defn.operators.SqlOperator; 8 | import edu.cmu.cs.db.peloton.test.generate.util.RandomUtils; 9 | 10 | import java.util.*; 11 | import java.util.function.Function; 12 | import java.util.stream.Collectors; 13 | 14 | import static com.google.common.base.Preconditions.*; 15 | 16 | /** 17 | * Created by tianyuli on 4/1/17. 18 | */ 19 | public class SearchCondition implements Ast.Elem { 20 | private static final double EARLY_TERMINATION_PROB = 0.1; 21 | private final Set allowedOperators; 22 | private final int limit; 23 | 24 | public SearchCondition(Set allowedOperators, int limit) { 25 | checkArgument(limit > 0); 26 | this.allowedOperators = allowedOperators; 27 | this.limit = limit; 28 | } 29 | 30 | @Override 31 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 32 | return generate(db, context, random, 33 | Ast.ExpressionType.BOOLEAN, 34 | limit, 35 | ConstantOption.NO_RESTRICTION).map(s -> new Ast.Clause(s, context)); 36 | } 37 | 38 | public Optional generate(DatabaseDefinition db, 39 | Context context, 40 | Random random, 41 | Ast.ExpressionType type, 42 | int depth, 43 | ConstantOption constantOption) { 44 | Optional operatorOption = getRandomOperator(type, random); 45 | if (!operatorOption.isPresent()) { 46 | return Optional.empty(); 47 | } 48 | SqlOperator operator = operatorOption.get(); 49 | 50 | if (depth == 1 || random.nextDouble() < EARLY_TERMINATION_PROB) { 51 | return baseCase(operator, db, context, random, type, constantOption); 52 | } 53 | return generateOperation(operator, i -> generate(db, context, random, 54 | operator.getArgType(i), depth - 1, constantOption)); 55 | 56 | } 57 | 58 | private Optional baseCase(SqlOperator operator, 59 | DatabaseDefinition db, 60 | Context context, 61 | Random random, 62 | Ast.ExpressionType type, 63 | ConstantOption constantOption) { 64 | String[] args = new String[operator.getArity()]; 65 | switch (ConstantOption.upperBound(constantOption, operator.getConstantOption())) { 66 | case NO_CONSTANT: 67 | return generateOperation(operator, i -> RandomUtils.noConstantOf(db, context, operator.getArgType(i), random)); 68 | case NOT_ALL_CONSTANTS: 69 | boolean notConstant = false; 70 | for (int i = 0; i < operator.getArity(); i++) { 71 | Optional arg = RandomUtils.noConstantOf(db, context, operator.getArgType(i), random); 72 | if (!arg.isPresent()) { 73 | args[i] = RandomUtils.constantOf(operator.getArgType(i), random); 74 | } else { 75 | args[i] = arg.get(); 76 | notConstant = true; 77 | } 78 | } 79 | if (!notConstant) { 80 | return Optional.empty(); 81 | } 82 | return Optional.of(operator.format(args)); 83 | case NO_RESTRICTION: 84 | return generateOperation(operator, i -> Optional.of(RandomUtils.valueOf(db, context, operator.getArgType(i), random))); 85 | default: 86 | throw new IllegalArgumentException("Unrecognized enum element " + constantOption); 87 | } 88 | } 89 | 90 | private Optional getRandomOperator(Ast.ExpressionType type, Random random) { 91 | Set operatorsMatchingType = allowedOperators.stream() 92 | .filter(s -> s.getResultType().matches(type)) 93 | .collect(Collectors.toSet()); 94 | if (operatorsMatchingType.isEmpty()) { 95 | return Optional.empty(); 96 | } else { 97 | return Optional.of(RandomUtils.randomElement(operatorsMatchingType, random)); 98 | } 99 | } 100 | 101 | private Optional generateOperation(SqlOperator operator, Function> argGenerator) { 102 | String[] args = new String[operator.getArity()]; 103 | for (int i = 0; i < operator.getArity(); i++) { 104 | Optional arg = argGenerator.apply(i); 105 | if (!arg.isPresent()) { 106 | return Optional.empty(); 107 | } 108 | args[i] = arg.get(); 109 | } 110 | return Optional.of(operator.format(args)); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/Select.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import com.google.common.collect.Sets; 5 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 6 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 7 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 8 | import edu.cmu.cs.db.peloton.test.generate.ast.ListElem; 9 | import edu.cmu.cs.db.peloton.test.generate.ast.ProductElem; 10 | import edu.cmu.cs.db.peloton.test.generate.defn.operators.SqlBinaryOperator; 11 | import edu.cmu.cs.db.peloton.test.generate.defn.operators.SqlUnaryOperator; 12 | 13 | import java.sql.JDBCType; 14 | import java.util.EnumSet; 15 | import java.util.List; 16 | import java.util.Random; 17 | 18 | /** 19 | * Created by tianyuli on 4/1/17. 20 | */ 21 | public class Select extends ProductElem { 22 | 23 | public static void main(String[] args) { 24 | DatabaseDefinition db = new DatabaseDefinition.Builder() 25 | .table("foo") 26 | .column("id", JDBCType.INTEGER) 27 | .column("value", JDBCType.VARCHAR) 28 | .table("bar") 29 | .column("id", JDBCType.INTEGER) 30 | .column("value", JDBCType.DOUBLE) 31 | .build(); 32 | Random random = new Random(); 33 | Select select = new Select(); 34 | for (int i = 0; i < 20; i++) { 35 | System.out.println(select.generateUntilPresent(db, Context.EMPTY, random).getClause()); 36 | } 37 | } 38 | 39 | @Override 40 | protected ImmutableList args() { 41 | return ImmutableList.of( 42 | new FromProp(), 43 | new SelectProp(), 44 | new SearchCondition(Sets.union(EnumSet.allOf(SqlBinaryOperator.class), 45 | EnumSet.allOf(SqlUnaryOperator.class)), 3)); 46 | } 47 | 48 | @Override 49 | protected String format(List args) { 50 | return String.format("SELECT %s FROM %s WHERE %s;", args.get(1), args.get(0), args.get(2)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/SelectProp.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.SumElem; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by tianyuli on 4/1/17. 11 | */ 12 | public class SelectProp extends SumElem { 13 | @Override 14 | protected List args() { 15 | return ImmutableList.of(new Star(), new PropertySpec()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/Star.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 6 | 7 | import java.util.Optional; 8 | import java.util.Random; 9 | 10 | /** 11 | * Created by tianyuli on 4/1/17. 12 | */ 13 | public class Star implements Ast.Elem { 14 | @Override 15 | public Optional generate(DatabaseDefinition db, Context context, Random random) { 16 | return Optional.of(new Ast.Clause("*", getNewContext(db, context))); 17 | } 18 | 19 | private static Context getNewContext(DatabaseDefinition db, Context context) { 20 | Context.Builder result = new Context.Builder(); 21 | result.putAll(context); 22 | for (String table : context.valuesOf(Ast.Sort.TABLE)) { 23 | for (String column : db.getTable(table).keySet()) { 24 | result.put(Ast.Sort.COLUMN, String.format("%s.%s", table, column)); 25 | } 26 | } 27 | return result.build(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/operators/ConstantOption.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn.operators; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Created by tianyuli on 5/1/17. 7 | */ 8 | public enum ConstantOption { 9 | NO_CONSTANT, NOT_ALL_CONSTANTS, NO_RESTRICTION; 10 | 11 | private static Map> gt; 12 | static { 13 | gt = new EnumMap<>(ConstantOption.class); 14 | gt.put(NO_CONSTANT, EnumSet.of(NOT_ALL_CONSTANTS, NO_RESTRICTION)); 15 | gt.put(NOT_ALL_CONSTANTS, EnumSet.of(NO_RESTRICTION)); 16 | gt.put(NO_RESTRICTION, EnumSet.noneOf(ConstantOption.class)); 17 | } 18 | 19 | public static ConstantOption upperBound(ConstantOption... constantOptions) { 20 | return Collections.max(Arrays.asList(constantOptions), (x, y) -> { 21 | if (x == y) { 22 | return 0; 23 | } 24 | return gt.get(x).contains(y) ? 1 : -1; 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/operators/SqlBinaryOperator.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn.operators; 2 | 3 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 4 | 5 | import static com.google.common.base.Preconditions.*; 6 | import static edu.cmu.cs.db.peloton.test.generate.ast.Ast.ExpressionType.*; 7 | 8 | /** 9 | * Created by tianyuli on 5/1/17. 10 | */ 11 | public enum SqlBinaryOperator implements SqlOperator { 12 | // TODO add more operators 13 | PLUS("+", NUMERIC, NUMERIC, NUMERIC, ConstantOption.NO_RESTRICTION), 14 | MINUS("-", NUMERIC, NUMERIC, NUMERIC, ConstantOption.NO_RESTRICTION), 15 | TIMES("*", NUMERIC, NUMERIC, NUMERIC, ConstantOption.NO_RESTRICTION), 16 | DIV("/", NUMERIC, NUMERIC, NUMERIC, ConstantOption.NO_RESTRICTION), 17 | CONCAT("||", VARCHAR, VARCHAR, VARCHAR, ConstantOption.NOT_ALL_CONSTANTS), 18 | EQ_NUM("=", NUMERIC, NUMERIC, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 19 | EQ_VARCHAR("=", VARCHAR, VARCHAR, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 20 | NOT_EQ_NUM("!=", NUMERIC, NUMERIC, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 21 | NOT_EQ_VARCHAR("!=", VARCHAR, VARCHAR, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 22 | GT_NUM(">", NUMERIC, NUMERIC, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 23 | GT_VARCHAR(">", VARCHAR, VARCHAR, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 24 | LT_NUM("<", NUMERIC, NUMERIC, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 25 | LT_VARCHAR("<", VARCHAR, VARCHAR, BOOLEAN, ConstantOption.NOT_ALL_CONSTANTS), 26 | AND("AND", BOOLEAN, BOOLEAN, BOOLEAN, ConstantOption.NO_CONSTANT), 27 | OR("OR", BOOLEAN, BOOLEAN, BOOLEAN, ConstantOption.NO_CONSTANT); 28 | 29 | 30 | private final String op; 31 | private final Ast.ExpressionType arg1, arg2, result; 32 | private final ConstantOption allowConstant; 33 | 34 | 35 | SqlBinaryOperator(String op, 36 | Ast.ExpressionType arg1, 37 | Ast.ExpressionType arg2, 38 | Ast.ExpressionType result, 39 | ConstantOption allowConstant) { 40 | this.op = op; 41 | this.arg1 = arg1; 42 | this.arg2 = arg2; 43 | this.result = result; 44 | this.allowConstant = allowConstant; 45 | } 46 | 47 | @Override 48 | public int getArity() { 49 | return 2; 50 | } 51 | 52 | @Override 53 | public Ast.ExpressionType getArgType(int i) { 54 | switch (i) { 55 | case 0: 56 | return arg1; 57 | case 1: 58 | return arg2; 59 | default: 60 | throw new IndexOutOfBoundsException("Operator only has " + getArity() + " argument(s)"); 61 | } 62 | } 63 | 64 | @Override 65 | public Ast.ExpressionType getResultType() { 66 | return result; 67 | } 68 | 69 | @Override 70 | public String format(String... args) { 71 | checkArgument(args.length == 2); 72 | return String.format("(%s %s %s)", args[0], op, args[1]); 73 | } 74 | 75 | @Override 76 | public ConstantOption getConstantOption() { 77 | return allowConstant; 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/operators/SqlOperator.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn.operators; 2 | 3 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 4 | 5 | /** 6 | * Created by tianyuli on 5/1/17. 7 | */ 8 | public interface SqlOperator { 9 | int getArity(); 10 | 11 | Ast.ExpressionType getArgType(int i); 12 | 13 | Ast.ExpressionType getResultType(); 14 | 15 | String format(String... args); 16 | 17 | ConstantOption getConstantOption(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/defn/operators/SqlUnaryOperator.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.defn.operators; 2 | 3 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 4 | 5 | import java.util.function.Function; 6 | 7 | import static com.google.common.base.Preconditions.*; 8 | import static edu.cmu.cs.db.peloton.test.generate.ast.Ast.ExpressionType.*; 9 | 10 | /** 11 | * Created by tianyuli on 5/1/17. 12 | */ 13 | public enum SqlUnaryOperator implements SqlOperator { 14 | POS(arg -> String.format("(+%s)", arg), NUMERIC, NUMERIC, ConstantOption.NO_CONSTANT), 15 | NEG(arg -> String.format("(-%s)", arg), NUMERIC, NUMERIC, ConstantOption.NO_CONSTANT), 16 | NULL_CHECK(arg -> String.format("(%s IS NULL)", arg), ANY, BOOLEAN, ConstantOption.NO_CONSTANT), 17 | NOT(arg -> String.format("(NOT %s)", arg), BOOLEAN, BOOLEAN, ConstantOption.NO_CONSTANT); 18 | 19 | 20 | private final Function format; 21 | private final Ast.ExpressionType argType, resultType; 22 | private final ConstantOption allowConstant; 23 | 24 | SqlUnaryOperator(Function format, 25 | Ast.ExpressionType argType, 26 | Ast.ExpressionType resultType, 27 | ConstantOption allowConstant) { 28 | this.format = format; 29 | this.argType = argType; 30 | this.resultType = resultType; 31 | this.allowConstant = allowConstant; 32 | } 33 | 34 | @Override 35 | public int getArity() { 36 | return 1; 37 | } 38 | 39 | @Override 40 | public Ast.ExpressionType getArgType(int i) { 41 | if (i == 0) { 42 | return argType; 43 | } 44 | throw new IndexOutOfBoundsException("Operator only has " + getArity() + " argument(s)"); 45 | } 46 | 47 | @Override 48 | public Ast.ExpressionType getResultType() { 49 | return resultType; 50 | } 51 | 52 | @Override 53 | public String format(String... args) { 54 | checkArgument(args.length == 1); 55 | return format.apply(args[0]); 56 | } 57 | 58 | @Override 59 | public ConstantOption getConstantOption() { 60 | return allowConstant; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/util/Iterators.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.util; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 6 | 7 | import java.util.*; 8 | import java.util.function.BiFunction; 9 | import java.util.function.Function; 10 | 11 | import static com.google.common.base.Preconditions.checkArgument; 12 | 13 | /** 14 | * Static utility class for various convenient methods on iterators 15 | */ 16 | public final class Iterators { 17 | /** 18 | * Concatenates two iterators together to form a new iterator 19 | * 20 | * @param the type of elements in the two iterators 21 | * @param one the first iterator 22 | * @param two the second iterator 23 | * @return the resulting concatenated iterator in the order given 24 | */ 25 | public static Iterator chain(Iterator one, Iterator two) { 26 | return new Iterator() { 27 | @Override 28 | public boolean hasNext() { 29 | return one.hasNext() || two.hasNext(); 30 | } 31 | 32 | @Override 33 | public T next() { 34 | return one.hasNext() ? one.next() : two.next(); 35 | } 36 | }; 37 | } 38 | 39 | /** 40 | * Takes an iterator and lazily applies an operation to it to form a new iterator 41 | * 42 | * @param the type of elements in the original iterator 43 | * @param the type of elements in the resulting iterator 44 | * @param one the iterator to be mapped 45 | * @param mapper the mapper 46 | * @return mapped iterator 47 | */ 48 | public static Iterator map(Iterator one, Function mapper) { 49 | return new Iterator() { 50 | @Override 51 | public boolean hasNext() { 52 | return one.hasNext(); 53 | } 54 | 55 | @Override 56 | public E next() { 57 | return mapper.apply(one.next()); 58 | } 59 | }; 60 | } 61 | 62 | /** 63 | * Fold left on the given iterator, combining valuesOf using the given function and identity 64 | * 65 | * @param the type of elements in the original iterator 66 | * @param the resulting type of this operation 67 | * @param it the iterator 68 | * @param id the identity value to be used for type E 69 | * @param f the function that combines T and E into a E 70 | * @return f(xn, ... f(x2, f(x1, id))) assuming the iterator yields x1, x2, ... xn 71 | */ 72 | public static E foldLeft(Iterator it, E id, BiFunction f) { 73 | E result = id; 74 | while (it.hasNext()) { 75 | result = f.apply(it.next(), result); 76 | } 77 | return result; 78 | } 79 | 80 | /** 81 | * Collects an iterator into a list 82 | * 83 | * @param the type of elements in the iterator 84 | * @param it the iterator 85 | * @return the list 86 | */ 87 | public static List toList(Iterator it) { 88 | List result = new ArrayList<>(); 89 | while (it.hasNext()) { 90 | result.add(it.next()); 91 | } 92 | return result; 93 | } 94 | 95 | /** 96 | * Creates a clause from a list of clauses, connecting all contexts in a union and 97 | * put string clauses into a sql style list, i.e. (x, c1 and y c2) goes to "x, y", c1 union c2 98 | * 99 | * @param elems the clauses to be joined together 100 | * @return the resulting clause 101 | */ 102 | public static Ast.Clause fromList(List elems) { 103 | StringBuilder builder = new StringBuilder(); 104 | Context context = Context.EMPTY; 105 | for (Ast.Clause clause : elems) { 106 | builder.append(clause.getClause()).append(","); 107 | context = context.union(clause.getContext()); 108 | } 109 | builder.deleteCharAt(builder.length() - 1); 110 | return new Ast.Clause(builder.toString(), context); 111 | } 112 | 113 | /** 114 | * Convenient method for constructing an iterator. 115 | * 116 | * @param elems the elements in the iterator, in order 117 | * @param the type if elements in the iterator 118 | * @return an iterator that returns the elements given in that order 119 | */ 120 | @SafeVarargs 121 | public static Iterator of(E... elems) { 122 | return Arrays.asList(elems).iterator(); 123 | } 124 | 125 | private Iterators() { 126 | // should not instantiate 127 | } 128 | 129 | public static Iterator fromAst(Ast.Elem elem, int limit, DatabaseDefinition db, Random random) { 130 | return new Iterator() { 131 | private int i = 0; 132 | @Override 133 | public boolean hasNext() { 134 | return i < limit; 135 | } 136 | 137 | @Override 138 | public String next() { 139 | i++; 140 | return elem.generateUntilPresent(db, Context.EMPTY, random).getClause(); 141 | } 142 | }; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/edu/cmu/cs/db/peloton/test/generate/util/RandomUtils.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.util; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | import edu.cmu.cs.db.peloton.test.generate.ast.Ast; 5 | import edu.cmu.cs.db.peloton.test.generate.ast.Context; 6 | 7 | import java.util.*; 8 | import java.util.stream.Collectors; 9 | import java.util.stream.Stream; 10 | 11 | import static edu.cmu.cs.db.peloton.test.generate.ast.Ast.ExpressionType.BOOLEAN; 12 | import static edu.cmu.cs.db.peloton.test.generate.ast.Ast.ExpressionType.NUMERIC; 13 | import static edu.cmu.cs.db.peloton.test.generate.ast.Ast.ExpressionType.VARCHAR; 14 | 15 | /** 16 | * Created by tianyuli on 4/1/17. 17 | */ 18 | public final class RandomUtils { 19 | private static final double CONSTANT_PROB = 0.2; 20 | 21 | public static E randomElement(List elems, Random random) { 22 | return elems.get(random.nextInt(elems.size())); 23 | } 24 | 25 | public static E randomElement(Set elems, Random random) { 26 | return randomElement(elems.stream(), elems.size(), random); 27 | } 28 | 29 | public static E randomElement(Stream elems, int count, Random random) { 30 | return elems.skip(random.nextInt(count)) 31 | .findFirst() 32 | .orElseThrow(IllegalArgumentException::new); 33 | } 34 | 35 | public static E randomElement(Stream elems, Random random) { 36 | return randomElement(elems.collect(Collectors.toList()), random); 37 | } 38 | 39 | public static String constantOf(Ast.ExpressionType type, Random random) { 40 | switch (type) { 41 | case NUMERIC: 42 | return Double.toString(random.nextDouble()); 43 | case VARCHAR: 44 | int length = random.nextInt(10); 45 | StringBuilder result = new StringBuilder("\'"); 46 | for (int i = 0; i < length; i++) { 47 | result.append((char) (random.nextInt(26) + 'a')); 48 | } 49 | result.append("\'"); 50 | return result.toString(); 51 | case BOOLEAN: 52 | return random.nextBoolean() ? "TRUE" : "FALSE"; 53 | case ANY: 54 | return constantOf(randomElement(Arrays.asList(NUMERIC, VARCHAR, BOOLEAN), random), random); 55 | default: 56 | throw new IllegalArgumentException("Unimplemented"); 57 | } 58 | } 59 | 60 | public static Optional noConstantOf(DatabaseDefinition db, 61 | Context context, 62 | Ast.ExpressionType type, 63 | Random random) { 64 | List columns = context.valuesOf(Ast.Sort.TABLE).stream() 65 | .flatMap(t -> db.getTable(t).entrySet().stream() 66 | .filter(e -> Ast.ExpressionType.fromJDBCType(e.getValue().getType()).matches(type)) 67 | .map(e -> t + "." + e.getKey())) 68 | .collect(Collectors.toList()); 69 | return columns.isEmpty() ? Optional.empty() : Optional.of(randomElement(columns, random)); 70 | } 71 | 72 | 73 | public static String valueOf(DatabaseDefinition db, Context context, Ast.ExpressionType type, Random random) { 74 | if (random.nextDouble() < CONSTANT_PROB) { 75 | return constantOf(type, random); 76 | } 77 | return noConstantOf(db, context, type, random).orElseGet(() -> constantOf(type, random)); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/edu/cmu/cs/db/peloton/test/generate/ast/ProductElemTest.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | 4 | public class ProductElemTest { 5 | // @Test 6 | // public void allClauses() throws Exception { 7 | // Ast.Elem left = new StubElem("foo", "bar"); 8 | // Ast.Elem right = new StubElem("1", "2"); 9 | // Ast.Elem tested = new ProductElem() { 10 | // @Override 11 | // protected ImmutableList args() { 12 | // return ImmutableList.of(left, right); 13 | // } 14 | // 15 | // @Override 16 | // protected String format(List args) { 17 | // return String.format("%s op %s", args.get(0), args.get(1)); 18 | // } 19 | // }; 20 | // 21 | // List values = Iterators.toList(tested.allClauses(null, Context.EMPTY, 0)); 22 | // assertTrue(values.contains(clauseOf("foo op 1"))); 23 | // assertTrue(values.contains(clauseOf("foo op 2"))); 24 | // assertTrue(values.contains(clauseOf("bar op 1"))); 25 | // assertTrue(values.contains(clauseOf("bar op 2"))); 26 | // assertEquals(4, values.size()); 27 | // } 28 | 29 | } -------------------------------------------------------------------------------- /src/test/java/edu/cmu/cs/db/peloton/test/generate/ast/StubElem.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | import edu.cmu.cs.db.peloton.test.common.DatabaseDefinition; 4 | 5 | import java.util.Arrays; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by Tianyu on 3/15/17. 11 | */ 12 | //public class StubElem implements Ast.Elem { 13 | // private final List values; 14 | // 15 | // public StubElem(String... values) { 16 | // this.values = Arrays.asList(values); 17 | // } 18 | // 19 | // @Override 20 | // public Iterator allClauses(DatabaseDefinition db, Context context, int depth) { 21 | // return values.stream().map(s -> new Ast.Clause(s, context)).iterator(); 22 | // } 23 | //} 24 | -------------------------------------------------------------------------------- /src/test/java/edu/cmu/cs/db/peloton/test/generate/ast/SumElemTest.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | 4 | public class SumElemTest { 5 | // @Test 6 | // public void allClauses() throws Exception { 7 | // Ast.Elem left = new StubElem("foo", "bar"); 8 | // Ast.Elem right = new StubElem("baz", "qux"); 9 | // Ast.Elem tested = new SumElem() { 10 | // @Override 11 | // protected ImmutableList args() { 12 | // return ImmutableList.of(left, right); 13 | // } 14 | // }; 15 | // 16 | // List values = Iterators.toList(tested.allClauses(null, Context.EMPTY, 0)); 17 | // assertTrue(values.contains(clauseOf("foo"))); 18 | // assertTrue(values.contains(clauseOf("bar"))); 19 | // assertTrue(values.contains(clauseOf("baz"))); 20 | // assertTrue(values.contains(clauseOf("qux"))); 21 | // assertEquals(4, values.size()); 22 | // } 23 | 24 | } -------------------------------------------------------------------------------- /src/test/java/edu/cmu/cs/db/peloton/test/generate/ast/TestListElem.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | public class TestListElem { 4 | // @Test 5 | // public void testListElem() { 6 | // Ast.Elem test = new StubElem("foo", "bar", "42"); 7 | // List result = Iterators.toList(new ListElem(test).allClauses(null, Context.EMPTY, 0)); 8 | // assertTrue(result.contains(clauseOf("foo"))); 9 | // assertTrue(result.contains(clauseOf("bar"))); 10 | // assertTrue(result.contains(clauseOf("42"))); 11 | // assertTrue(result.contains(clauseOf("foo,bar"))); 12 | // assertTrue(result.contains(clauseOf("foo,42"))); 13 | // assertTrue(result.contains(clauseOf("bar,42"))); 14 | // assertTrue(result.contains(clauseOf("foo,bar,42"))); 15 | // assertEquals(7, result.size()); 16 | // } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/edu/cmu/cs/db/peloton/test/generate/ast/TestUtil.java: -------------------------------------------------------------------------------- 1 | package edu.cmu.cs.db.peloton.test.generate.ast; 2 | 3 | 4 | public class TestUtil { 5 | public static Ast.Clause clauseOf(String s) { 6 | return new Ast.Clause(s, Context.EMPTY); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_SORTED 2 /0=unsorted, 1=sorted, 2=foldcase/ 2 | -------------------------------------------------------------------------------- /test_out/foo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cmu-db/peloton-test/9da1ccf9b7c6971ba040004164b0a0929a850830/test_out/foo --------------------------------------------------------------------------------