diff --git a/.github/workflows/mini-sysbench.yml b/.github/workflows/mini-sysbench.yml new file mode 100644 index 0000000000..947e471d39 --- /dev/null +++ b/.github/workflows/mini-sysbench.yml @@ -0,0 +1,129 @@ +name: Mini Sysbench + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: write + +jobs: + mini-sysbench: + runs-on: ubuntu-latest + + steps: + - name: Checkout DoltgreSQL + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + + - name: Setup Git User + uses: fregante/setup-git-user@v2 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install Sysbench + run: | + curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh | sudo bash + sudo apt -y install sysbench + + - name: Test PR branch + id: test_doltgresql_pr + continue-on-error: true + run: | + ./postgres/parser/build.sh + ./scripts/quick_sysbench.sh + mv ./scripts/mini_sysbench/results.log ./scripts/mini_sysbench/results1.log + cat ./scripts/mini_sysbench/results1.log + + - name: Test main branch + id: test_doltgresql_main + continue-on-error: true + run: | + git reset --hard + git fetch --all --unshallow + git checkout origin/main + ./postgres/parser/build.sh + ./scripts/quick_sysbench.sh + mv ./scripts/mini_sysbench/results.log ./scripts/mini_sysbench/results2.log + cat ./scripts/mini_sysbench/results2.log + + - name: Check Sysbench Logs + id: check_logs + run: | + cd scripts/mini_sysbench + if [[ -f "results1.log" && -f "results2.log" ]]; then + echo "logs_exist=true" >> $GITHUB_OUTPUT + echo "logs exist" + else + echo "logs_exist=false" >> $GITHUB_OUTPUT + echo "One of the branches could not successfully run the benchmarks." + echo "Please review them for errors, which should be fixed." + exit 1 + fi + + - name: Build Sysbench Results Comment + id: build_results + if: steps.check_logs.outputs.logs_exist == 'true' + run: | + cd testing/go/benchmark + output=$(go run .) + echo "program_output<> $GITHUB_OUTPUT + echo "$output" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "$output" + + - name: Is PR From Fork + id: from_fork + run: | + if [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then + echo "This is running from a fork, skipping commenting" + echo "fork=true" >> $GITHUB_OUTPUT + else + echo "This is not running from a fork" + echo "fork=false" >> $GITHUB_OUTPUT + fi + + - name: Post Comment + if: steps.from_fork.outputs.fork == 'false' && steps.build_results.outputs.program_output + uses: actions/github-script@v6 + env: + PROGRAM_OUTPUT: ${{ steps.build_results.outputs.program_output }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const commentMarker = '' + const output = process.env.PROGRAM_OUTPUT + const body = `${commentMarker}\n${output}` + + // List comments on the PR + const { data: comments } = await github.rest.issues.listComments({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + }) + + // Check if a comment already exists + const comment = comments.find(comment => comment.body.includes(commentMarker)) + + if (comment) { + // Update the existing comment + await github.rest.issues.updateComment({ + comment_id: comment.id, + owner: context.repo.owner, + repo: context.repo.repo, + body: body + }) + } else { + // Create a new comment + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: body + }) + } diff --git a/.gitignore b/.gitignore index 7401ddafc9..8eeff22064 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,9 @@ integration-tests/bats/batsee_results testing/logictest/*.log testing/go/regression/out +# ignore sysbench +scripts/mini_sysbench + # ignore doltgres db created doltgres postgres diff --git a/go.mod b/go.mod index 66ec3bb024..422329d238 100644 --- a/go.mod +++ b/go.mod @@ -8,13 +8,13 @@ require ( github.com/PuerkitoBio/goquery v1.8.1 github.com/cockroachdb/apd/v2 v2.0.3-0.20200518165714-d020e156310a github.com/cockroachdb/errors v1.7.5 - github.com/dolthub/dolt/go v0.40.5-0.20241114235619-0995efed23b9 - github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20241104143128-c2bb78c109df + github.com/dolthub/dolt/go v0.40.5-0.20241122201136-4ad19572a805 + github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20241119094239-f4e529af734d github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 github.com/dolthub/go-icu-regex v0.0.0-20240916130659-0118adc6b662 - github.com/dolthub/go-mysql-server v0.18.2-0.20241114232015-87d29acb3d67 + github.com/dolthub/go-mysql-server v0.18.2-0.20241122190136-dd8defd838e3 github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 - github.com/dolthub/vitess v0.0.0-20241111235433-a20a5ab9d7c9 + github.com/dolthub/vitess v0.0.0-20241121221517-3e7b5ffc22b0 github.com/fatih/color v1.13.0 github.com/goccy/go-json v0.10.2 github.com/gogo/protobuf v1.3.2 @@ -32,14 +32,14 @@ require ( github.com/sergi/go-diff v1.1.0 github.com/shopspring/decimal v1.3.1 github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/twpayne/go-geom v1.3.6 github.com/xdg-go/stringprep v1.0.4 golang.org/x/crypto v0.23.0 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/net v0.25.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.20.0 + golang.org/x/sys v0.27.0 golang.org/x/text v0.16.0 gopkg.in/src-d/go-errors.v1 v1.0.0 gopkg.in/yaml.v2 v2.4.0 @@ -77,7 +77,7 @@ require ( github.com/dustin/go-humanize v1.0.1 // indirect github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/go-kit/kit v0.10.0 // indirect - github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-sql-driver/mysql v1.7.2-0.20231213112541-0004702b931d // indirect github.com/gocraft/dbr/v2 v2.7.2 // indirect @@ -90,7 +90,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.4 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect github.com/googleapis/gax-go/v2 v2.11.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect @@ -142,8 +142,9 @@ require ( github.com/xitongsys/parquet-go-source v0.0.0-20211010230925-397910c5e371 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.7.0 // indirect - go.opentelemetry.io/otel/trace v1.7.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect diff --git a/go.sum b/go.sum index f35d3fe881..589a6414d0 100644 --- a/go.sum +++ b/go.sum @@ -214,18 +214,18 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dolthub/dolt/go v0.40.5-0.20241114235619-0995efed23b9 h1:9B26h5cfQMDZvEw2ZrQN1+MYqPZMNXJzgdSXdT+VgzM= -github.com/dolthub/dolt/go v0.40.5-0.20241114235619-0995efed23b9/go.mod h1:AJRhYyewJAejq+sd74zLcL3piCOBkkUwwp4iR6E+aPs= -github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20241104143128-c2bb78c109df h1:xafyaNR+hSk5TwOhmNkhhrmOZKIOkxAOCiIEUzlIybc= -github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20241104143128-c2bb78c109df/go.mod h1:L5RDYZbC9BBWmoU2+TjTekeqqhFXX5EqH9ln00O0stY= +github.com/dolthub/dolt/go v0.40.5-0.20241122201136-4ad19572a805 h1:89YAwmuEQ6B9tsQp6UAAKUSVrDD+w3GedW7V14d3RuU= +github.com/dolthub/dolt/go v0.40.5-0.20241122201136-4ad19572a805/go.mod h1:ImVR1GtrJTVzmsnsJoDaZpLiVdLR+xXar7F2dur5oE8= +github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20241119094239-f4e529af734d h1:gO9+wrmNHXukPNCO1tpfCcXIdMlW/qppbUStfLvqz/U= +github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi v0.0.0-20241119094239-f4e529af734d/go.mod h1:L5RDYZbC9BBWmoU2+TjTekeqqhFXX5EqH9ln00O0stY= github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2 h1:u3PMzfF8RkKd3lB9pZ2bfn0qEG+1Gms9599cr0REMww= github.com/dolthub/flatbuffers/v23 v23.3.3-dh.2/go.mod h1:mIEZOHnFx4ZMQeawhw9rhsj+0zwQj7adVsnBX7t+eKY= github.com/dolthub/fslock v0.0.3 h1:iLMpUIvJKMKm92+N1fmHVdxJP5NdyDK5bK7z7Ba2s2U= github.com/dolthub/fslock v0.0.3/go.mod h1:QWql+P17oAAMLnL4HGB5tiovtDuAjdDTPbuqx7bYfa0= github.com/dolthub/go-icu-regex v0.0.0-20240916130659-0118adc6b662 h1:aC17hZD6iwzBwwfO5M+3oBT5E5gGRiQPdn+vzpDXqIA= github.com/dolthub/go-icu-regex v0.0.0-20240916130659-0118adc6b662/go.mod h1:KPUcpx070QOfJK1gNe0zx4pA5sicIK1GMikIGLKC168= -github.com/dolthub/go-mysql-server v0.18.2-0.20241114232015-87d29acb3d67 h1:bl9C66VxMQVd3cyS6Owy4IE9XeSvFbm2/PaJreWI1eA= -github.com/dolthub/go-mysql-server v0.18.2-0.20241114232015-87d29acb3d67/go.mod h1:sOMQzWUvHvJECzpcUxjDgV5BR/A7U+hOh596PUO2NPI= +github.com/dolthub/go-mysql-server v0.18.2-0.20241122190136-dd8defd838e3 h1:bvSE64pO6euDX8j5hpml5qVVz9OXG3hVV9532bn+eZ0= +github.com/dolthub/go-mysql-server v0.18.2-0.20241122190136-dd8defd838e3/go.mod h1:2mA/v84EOCe8TQIKR8TN8ZRIQSbOqThGQHyevGRmawU= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63 h1:OAsXLAPL4du6tfbBgK0xXHZkOlos63RdKYS3Sgw/dfI= github.com/dolthub/gozstd v0.0.0-20240423170813-23a2903bca63/go.mod h1:lV7lUeuDhH5thVGDCKXbatwKy2KW80L4rMT46n+Y2/Q= github.com/dolthub/ishell v0.0.0-20240701202509-2b217167d718 h1:lT7hE5k+0nkBdj/1UOSFwjWpNxf+LCApbRHgnCA17XE= @@ -238,8 +238,8 @@ github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216 h1:JWkKRE4 github.com/dolthub/sqllogictest/go v0.0.0-20240618184124-ca47f9354216/go.mod h1:e/FIZVvT2IR53HBCAo41NjqgtEnjMJGKca3Y/dAmZaA= github.com/dolthub/swiss v0.1.0 h1:EaGQct3AqeP/MjASHLiH6i4TAmgbG/c4rA6a1bzCOPc= github.com/dolthub/swiss v0.1.0/go.mod h1:BeucyB08Vb1G9tumVN3Vp/pyY4AMUnr9p7Rz7wJ7kAQ= -github.com/dolthub/vitess v0.0.0-20241111235433-a20a5ab9d7c9 h1:s36zDuLPuZRWC0nBCJs2Z8joP19eKEtcsIsuE8K9Kx0= -github.com/dolthub/vitess v0.0.0-20241111235433-a20a5ab9d7c9/go.mod h1:uBvlRluuL+SbEWTCZ68o0xvsdYZER3CEG/35INdzfJM= +github.com/dolthub/vitess v0.0.0-20241121221517-3e7b5ffc22b0 h1:C8X4RkkWKcrJG6rG+MsdFINX2PhB7ObpbBvFcWsI8K8= +github.com/dolthub/vitess v0.0.0-20241121221517-3e7b5ffc22b0/go.mod h1:alcJgfdyIhFaAiYyEmuDCFSLCzedz3KCaIclLoCUtJg= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -298,8 +298,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= @@ -393,7 +393,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs= @@ -423,8 +422,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -844,8 +843,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -856,8 +856,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= github.com/tetratelabs/wazero v1.1.0 h1:EByoAhC+QcYpwSZJSs/aV0uokxPwBgKxfiokSUwAknQ= @@ -938,10 +939,12 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM= -go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk= -go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -1167,8 +1170,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= diff --git a/scripts/quick_sysbench.sh b/scripts/quick_sysbench.sh new file mode 100755 index 0000000000..635695dd3c --- /dev/null +++ b/scripts/quick_sysbench.sh @@ -0,0 +1,81 @@ +#!/bin/bash +#set -e +#set -o pipefail + +PORT=54171 + +# Set the working directory to the directory of the script's location +cd "$(cd -P -- "$(dirname -- "$0")" && pwd -P)" + +mkdir -p mini_sysbench +cd mini_sysbench + +if [ ! -d "./sysbench-lua-scripts" ]; then + git clone https://github.com/dolthub/sysbench-lua-scripts.git +fi +cp ./sysbench-lua-scripts/*.lua ./ + +go build -o doltgres.exe ../../cmd/doltgres/ + +values=("covering_index_scan_postgres" "index_join_postgres" "index_join_scan_postgres" "index_scan_postgres" "oltp_point_select" "oltp_read_only" "select_random_points" "select_random_ranges" "table_scan_postgres" "types_table_scan_postgres") +for value in "${values[@]}"; do + SYSBENCH_TEST="$value" + cat < dolt-config.yaml +log_level: debug + +behavior: + read_only: false + disable_client_multi_statements: false + dolt_transaction_commit: false + +user: + name: "postgres" + password: "password" + +listener: + host: localhost + port: $PORT + read_timeout_millis: 28800000 + write_timeout_millis: 28800000 + +data_dir: . +YAML + + rm -rf ./.dolt + rm -rf ./postgres + ./doltgres.exe -config="dolt-config.yaml" 2> prepare.log & + SERVER_PID="$!" + + sleep 1 + echo "----$SYSBENCH_TEST----" + sysbench \ + --db-driver="pgsql" \ + --pgsql-host="0.0.0.0" \ + --pgsql-port="$PORT" \ + --pgsql-user="postgres" \ + --pgsql-password="password" \ + --pgsql-db="postgres" \ + "$SYSBENCH_TEST" prepare + + kill -15 "$SERVER_PID" + + echo "----$SYSBENCH_TEST----" 1>> results.log + ./doltgres.exe -config="dolt-config.yaml" 2> run.log & + SERVER_PID="$!" + sleep 1 + + sysbench \ + --db-driver="pgsql" \ + --pgsql-host="0.0.0.0" \ + --pgsql-port="$PORT" \ + --pgsql-user="postgres" \ + --pgsql-password="password" \ + --pgsql-db="postgres" \ + --time=15 \ + --db-ps-mode=disable \ + "$SYSBENCH_TEST" run 1>> results.log + + sleep 1 + kill -15 "$SERVER_PID" + echo "----$SYSBENCH_TEST----" 1>> results.log +done diff --git a/server/ast/aliased_table_expr.go b/server/ast/aliased_table_expr.go index a12af43f2c..25373c733b 100644 --- a/server/ast/aliased_table_expr.go +++ b/server/ast/aliased_table_expr.go @@ -43,8 +43,8 @@ func nodeAliasedTableExpr(ctx *Context, node *tree.AliasedTableExpr) (*vitess.Al aliasExpr = tableName authInfo = vitess.AuthInformation{ AuthType: ctx.Auth().PeekAuthType(), - TargetType: auth.AuthTargetType_SingleTableIdentifier, - TargetNames: []string{tableName.SchemaQualifier.String(), tableName.Name.String()}, + TargetType: auth.AuthTargetType_TableIdentifiers, + TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String(), tableName.Name.String()}, } case *tree.Subquery: tableExpr, err := nodeTableExpr(ctx, expr) diff --git a/server/ast/create_schema.go b/server/ast/create_schema.go index ba982f5064..f2622adc7a 100644 --- a/server/ast/create_schema.go +++ b/server/ast/create_schema.go @@ -17,6 +17,8 @@ package ast import ( vitess "github.com/dolthub/vitess/go/vt/sqlparser" + "github.com/dolthub/doltgresql/server/auth" + "github.com/dolthub/doltgresql/postgres/parser/sem/tree" ) @@ -25,13 +27,16 @@ func nodeCreateSchema(ctx *Context, node *tree.CreateSchema) (vitess.Statement, if node == nil { return nil, nil } - return &vitess.DBDDL{ Action: "CREATE", SchemaOrDatabase: "schema", DBName: node.Schema, IfNotExists: node.IfNotExists, CharsetCollate: nil, // TODO - // TODO: AuthRole + Auth: vitess.AuthInformation{ + AuthType: auth.AuthType_CREATE, + TargetType: auth.AuthTargetType_DatabaseIdentifiers, + TargetNames: []string{""}, + }, }, nil } diff --git a/server/ast/create_table.go b/server/ast/create_table.go index c5c7e5cab2..b1f9261b46 100644 --- a/server/ast/create_table.go +++ b/server/ast/create_table.go @@ -20,6 +20,7 @@ import ( vitess "github.com/dolthub/vitess/go/vt/sqlparser" "github.com/dolthub/doltgresql/postgres/parser/sem/tree" + "github.com/dolthub/doltgresql/server/auth" ) // nodeCreateTable handles *tree.CreateTable nodes. @@ -87,6 +88,11 @@ func nodeCreateTable(ctx *Context, node *tree.CreateTable) (*vitess.DDL, error) Temporary: isTemporary, OptSelect: optSelect, OptLike: optLike, + Auth: vitess.AuthInformation{ + AuthType: auth.AuthType_CREATE, + TargetType: auth.AuthTargetType_SchemaIdentifiers, + TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String()}, + }, } if err = assignTableDefs(ctx, node.Defs, ddl); err != nil { return nil, err @@ -101,9 +107,11 @@ func nodeCreateTable(ctx *Context, node *tree.CreateTable) (*vitess.DDL, error) } // GMS does not support PARTITION BY, so we parse it and ignore it - ddl.TableSpec.PartitionOpt = &vitess.PartitionOption{ - PartitionType: string(node.PartitionBy.Type), - Expr: vitess.NewColName(string(node.PartitionBy.Elems[0].Column)), + if ddl.TableSpec != nil { + ddl.TableSpec.PartitionOpt = &vitess.PartitionOption{ + PartitionType: string(node.PartitionBy.Type), + Expr: vitess.NewColName(string(node.PartitionBy.Elems[0].Column)), + } } } if node.PartitionOf.Table() != "" { diff --git a/server/ast/drop_table.go b/server/ast/drop_table.go index ad815959e6..5e52c28fbd 100644 --- a/server/ast/drop_table.go +++ b/server/ast/drop_table.go @@ -20,6 +20,7 @@ import ( vitess "github.com/dolthub/vitess/go/vt/sqlparser" "github.com/dolthub/doltgresql/postgres/parser/sem/tree" + "github.com/dolthub/doltgresql/server/auth" ) // nodeDropTable handles *tree.DropTable nodes. @@ -36,16 +37,24 @@ func nodeDropTable(ctx *Context, node *tree.DropTable) (*vitess.DDL, error) { return nil, fmt.Errorf("CASCADE is not yet supported") } tableNames := make([]vitess.TableName, len(node.Names)) + authTableNames := make([]string, 0, len(node.Names)*3) for i := range node.Names { var err error tableNames[i], err = nodeTableName(ctx, &node.Names[i]) if err != nil { return nil, err } + authTableNames = append(authTableNames, + tableNames[i].DbQualifier.String(), tableNames[i].SchemaQualifier.String(), tableNames[i].Name.String()) } return &vitess.DDL{ Action: vitess.DropStr, FromTables: tableNames, IfExists: node.IfExists, + Auth: vitess.AuthInformation{ + AuthType: auth.AuthType_DROPTABLE, + TargetType: auth.AuthTargetType_Ignore, + TargetNames: authTableNames, + }, }, nil } diff --git a/server/ast/expr.go b/server/ast/expr.go index d065a1df59..c94da8f335 100644 --- a/server/ast/expr.go +++ b/server/ast/expr.go @@ -727,7 +727,7 @@ func nodeExpr(ctx *Context, node tree.Expr) (vitess.Expr, error) { Expression: unknownLiteral, }, nil case *tree.Subquery: - return nodeSubquery(ctx, node) + return nodeSubqueryOrExists(ctx, node) case *tree.Tuple: if len(node.Labels) > 0 { return nil, fmt.Errorf("tuple labels are not yet supported") diff --git a/server/ast/grant.go b/server/ast/grant.go index c42f243509..b4d6c405ab 100644 --- a/server/ast/grant.go +++ b/server/ast/grant.go @@ -32,10 +32,12 @@ func nodeGrant(ctx *Context, node *tree.Grant) (vitess.Statement, error) { return nil, nil } var grantTable *pgnodes.GrantTable + var grantSchema *pgnodes.GrantSchema + var grantDatabase *pgnodes.GrantDatabase switch node.Targets.TargetType { case privilege.Table: - tables := make([]doltdb.TableName, len(node.Targets.Tables)) - for i, table := range node.Targets.Tables { + tables := make([]doltdb.TableName, 0, len(node.Targets.Tables)+len(node.Targets.InSchema)) + for _, table := range node.Targets.Tables { normalizedTable, err := table.NormalizeTablePattern() if err != nil { return nil, err @@ -45,24 +47,50 @@ func nodeGrant(ctx *Context, node *tree.Grant) (vitess.Statement, error) { if normalizedTable.ExplicitCatalog { return nil, fmt.Errorf("granting privileges to other databases is not yet supported") } - tables[i] = doltdb.TableName{ + tables = append(tables, doltdb.TableName{ Name: string(normalizedTable.ObjectName), Schema: string(normalizedTable.SchemaName), - } + }) case *tree.AllTablesSelector: - return nil, fmt.Errorf("selecting all tables in a schema is not yet supported") + tables = append(tables, doltdb.TableName{ + Name: "", + Schema: string(normalizedTable.SchemaName), + }) default: return nil, fmt.Errorf(`unexpected table type in GRANT: %T`, normalizedTable) } } + for _, schema := range node.Targets.InSchema { + tables = append(tables, doltdb.TableName{ + Name: "", + Schema: schema, + }) + } privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_TABLE, node.Privileges) if err != nil { return nil, err } grantTable = &pgnodes.GrantTable{ - Privileges: privileges, - Tables: tables, - AllTablesInSchemas: nil, + Privileges: privileges, + Tables: tables, + } + case privilege.Schema: + privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_SCHEMA, node.Privileges) + if err != nil { + return nil, err + } + grantSchema = &pgnodes.GrantSchema{ + Privileges: privileges, + Schemas: node.Targets.Names, + } + case privilege.Database: + privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_DATABASE, node.Privileges) + if err != nil { + return nil, err + } + grantDatabase = &pgnodes.GrantDatabase{ + Privileges: privileges, + Databases: node.Targets.Databases.ToStrings(), } default: return nil, fmt.Errorf("this form of GRANT is not yet supported") @@ -70,6 +98,9 @@ func nodeGrant(ctx *Context, node *tree.Grant) (vitess.Statement, error) { return vitess.InjectedStatement{ Statement: &pgnodes.Grant{ GrantTable: grantTable, + GrantSchema: grantSchema, + GrantDatabase: grantDatabase, + GrantRole: nil, ToRoles: node.Grantees, WithGrantOption: node.WithGrantOption, GrantedBy: node.GrantedBy, diff --git a/server/ast/grant_role.go b/server/ast/grant_role.go index f0c1b62eb9..9c7e5523ab 100644 --- a/server/ast/grant_role.go +++ b/server/ast/grant_role.go @@ -15,7 +15,7 @@ package ast import ( - "fmt" + pgnodes "github.com/dolthub/doltgresql/server/node" vitess "github.com/dolthub/vitess/go/vt/sqlparser" @@ -27,5 +27,15 @@ func nodeGrantRole(ctx *Context, node *tree.GrantRole) (vitess.Statement, error) if node == nil { return nil, nil } - return nil, fmt.Errorf("GRANT ROLE is not yet supported") + return vitess.InjectedStatement{ + Statement: &pgnodes.Grant{ + GrantRole: &pgnodes.GrantRole{ + Groups: node.Roles.ToStrings(), + }, + ToRoles: node.Members, + WithGrantOption: len(node.WithOption) > 0, + GrantedBy: node.GrantedBy, + }, + Children: nil, + }, nil } diff --git a/server/ast/insert.go b/server/ast/insert.go index 3b87ae7d9d..de64743077 100644 --- a/server/ast/insert.go +++ b/server/ast/insert.go @@ -89,8 +89,14 @@ func nodeInsert(ctx *Context, node *tree.Insert) (*vitess.Insert, error) { if vSelect, ok := rows.(*vitess.Select); ok && len(vSelect.From) == 1 { if aliasedStmt, ok := vSelect.From[0].(*vitess.AliasedTableExpr); ok { if valsStmt, ok := aliasedStmt.Expr.(*vitess.ValuesStatement); ok { + var vals vitess.Values + if len(valsStmt.Rows) == 0 { + vals = []vitess.ValTuple{{}} + } else { + vals = valsStmt.Rows + } rows = &vitess.AliasedValues{ - Values: valsStmt.Rows, + Values: vals, } } } @@ -105,8 +111,8 @@ func nodeInsert(ctx *Context, node *tree.Insert) (*vitess.Insert, error) { OnDup: onDuplicate, Auth: vitess.AuthInformation{ AuthType: auth.AuthType_INSERT, - TargetType: auth.AuthTargetType_SingleTableIdentifier, - TargetNames: []string{tableName.SchemaQualifier.String(), tableName.Name.String()}, + TargetType: auth.AuthTargetType_TableIdentifiers, + TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String(), tableName.Name.String()}, }, }, nil } diff --git a/server/ast/revoke.go b/server/ast/revoke.go index 84c5c47214..1bf0adc52b 100644 --- a/server/ast/revoke.go +++ b/server/ast/revoke.go @@ -32,9 +32,11 @@ func nodeRevoke(ctx *Context, node *tree.Revoke) (vitess.Statement, error) { return nil, nil } var revokeTable *pgnodes.RevokeTable + var revokeSchema *pgnodes.RevokeSchema + var revokeDatabase *pgnodes.RevokeDatabase switch node.Targets.TargetType { case privilege.Table: - tables := make([]doltdb.TableName, len(node.Targets.Tables)) + tables := make([]doltdb.TableName, len(node.Targets.Tables)+len(node.Targets.InSchema)) for i, table := range node.Targets.Tables { normalizedTable, err := table.NormalizeTablePattern() if err != nil { @@ -50,19 +52,45 @@ func nodeRevoke(ctx *Context, node *tree.Revoke) (vitess.Statement, error) { Schema: string(normalizedTable.SchemaName), } case *tree.AllTablesSelector: - return nil, fmt.Errorf("selecting all tables in a schema is not yet supported") + tables[i] = doltdb.TableName{ + Name: "", + Schema: string(normalizedTable.SchemaName), + } default: return nil, fmt.Errorf(`unexpected table type in REVOKE: %T`, normalizedTable) } } + for _, schema := range node.Targets.InSchema { + tables = append(tables, doltdb.TableName{ + Name: "", + Schema: schema, + }) + } privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_TABLE, node.Privileges) if err != nil { return nil, err } revokeTable = &pgnodes.RevokeTable{ - Privileges: privileges, - Tables: tables, - AllTablesInSchemas: nil, + Privileges: privileges, + Tables: tables, + } + case privilege.Schema: + privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_SCHEMA, node.Privileges) + if err != nil { + return nil, err + } + revokeSchema = &pgnodes.RevokeSchema{ + Privileges: privileges, + Schemas: node.Targets.Names, + } + case privilege.Database: + privileges, err := convertPrivilegeKinds(auth.PrivilegeObject_DATABASE, node.Privileges) + if err != nil { + return nil, err + } + revokeDatabase = &pgnodes.RevokeDatabase{ + Privileges: privileges, + Databases: node.Targets.Databases.ToStrings(), } default: return nil, fmt.Errorf("this form of REVOKE is not yet supported") @@ -70,6 +98,9 @@ func nodeRevoke(ctx *Context, node *tree.Revoke) (vitess.Statement, error) { return vitess.InjectedStatement{ Statement: &pgnodes.Revoke{ RevokeTable: revokeTable, + RevokeSchema: revokeSchema, + RevokeDatabase: revokeDatabase, + RevokeRole: nil, FromRoles: node.Grantees, GrantedBy: node.GrantedBy, GrantOptionFor: node.GrantOptionFor, diff --git a/server/ast/revoke_role.go b/server/ast/revoke_role.go index 94ca24eb6d..62b0c5f48d 100644 --- a/server/ast/revoke_role.go +++ b/server/ast/revoke_role.go @@ -15,7 +15,7 @@ package ast import ( - "fmt" + pgnodes "github.com/dolthub/doltgresql/server/node" vitess "github.com/dolthub/vitess/go/vt/sqlparser" @@ -27,5 +27,16 @@ func nodeRevokeRole(ctx *Context, node *tree.RevokeRole) (vitess.Statement, erro if node == nil { return nil, nil } - return nil, fmt.Errorf("REVOKE ROLE is not yet supported") + return vitess.InjectedStatement{ + Statement: &pgnodes.Revoke{ + RevokeRole: &pgnodes.RevokeRole{ + Groups: node.Roles.ToStrings(), + }, + FromRoles: node.Members, + GrantedBy: node.GrantedBy, + GrantOptionFor: len(node.Option) > 0, + Cascade: node.DropBehavior == tree.DropCascade, + }, + Children: nil, + }, nil } diff --git a/server/ast/select.go b/server/ast/select.go index 3d00d1b38e..78ea8f49ba 100644 --- a/server/ast/select.go +++ b/server/ast/select.go @@ -30,7 +30,9 @@ func nodeSelect(ctx *Context, node *tree.Select) (vitess.SelectStatement, error) return nil, nil } if node.Select == nil { - return nil, fmt.Errorf("internal: select clause should not be null") + node.Select = &tree.ValuesClause{ + Rows: []tree.Exprs{}, + } } selectStmt, err := nodeSelectStatement(ctx, node.Select) if err != nil { diff --git a/server/ast/subquery.go b/server/ast/subquery.go index 7ca20e16c1..02dff93e67 100644 --- a/server/ast/subquery.go +++ b/server/ast/subquery.go @@ -15,8 +15,6 @@ package ast import ( - "fmt" - vitess "github.com/dolthub/vitess/go/vt/sqlparser" "github.com/dolthub/doltgresql/postgres/parser/sem/tree" @@ -28,9 +26,6 @@ func nodeSubquery(ctx *Context, node *tree.Subquery) (*vitess.Subquery, error) { if node == nil { return nil, nil } - if node.Exists { - return nil, fmt.Errorf("EXISTS is not yet supported") - } selectStmt, err := nodeSelectStatement(ctx, node.Select) if err != nil { return nil, err @@ -51,3 +46,17 @@ func nodeSubqueryToTableExpr(ctx *Context, node *tree.Subquery) (vitess.TableExp As: vitess.NewTableIdent(utils.GenerateUniqueAlias()), }, nil } + +// nodeSubqueryOrExists handles *tree.Subquery nodes that may be an EXISTS subquery, returning a vitess.Expr. +func nodeSubqueryOrExists(ctx *Context, node *tree.Subquery) (vitess.Expr, error) { + subquery, err := nodeSubquery(ctx, node) + if err != nil { + return nil, err + } + if !node.Exists { + return subquery, nil + } + return &vitess.ExistsExpr{ + Subquery: subquery, + }, nil +} diff --git a/server/ast/table_expr.go b/server/ast/table_expr.go index ddffe0dd96..f73fdb6bd8 100644 --- a/server/ast/table_expr.go +++ b/server/ast/table_expr.go @@ -125,8 +125,8 @@ func nodeTableExpr(ctx *Context, node tree.TableExpr) (vitess.TableExpr, error) Expr: tableName, Auth: vitess.AuthInformation{ AuthType: ctx.Auth().PeekAuthType(), - TargetType: auth.AuthTargetType_SingleTableIdentifier, - TargetNames: []string{tableName.SchemaQualifier.String(), tableName.Name.String()}, + TargetType: auth.AuthTargetType_TableIdentifiers, + TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String(), tableName.Name.String()}, }, }, nil case *tree.TableRef: @@ -140,8 +140,8 @@ func nodeTableExpr(ctx *Context, node tree.TableExpr) (vitess.TableExpr, error) Expr: tableName, Auth: vitess.AuthInformation{ AuthType: ctx.Auth().PeekAuthType(), - TargetType: auth.AuthTargetType_SingleTableIdentifier, - TargetNames: []string{tableName.SchemaQualifier.String(), tableName.Name.String()}, + TargetType: auth.AuthTargetType_TableIdentifiers, + TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String(), tableName.Name.String()}, }, }, nil default: diff --git a/server/ast/truncate.go b/server/ast/truncate.go index 0101328291..1961457b0e 100644 --- a/server/ast/truncate.go +++ b/server/ast/truncate.go @@ -48,8 +48,8 @@ func nodeTruncate(ctx *Context, node *tree.Truncate) (*vitess.DDL, error) { Table: tableName, Auth: vitess.AuthInformation{ AuthType: auth.AuthType_TRUNCATE, - TargetType: auth.AuthTargetType_SingleTableIdentifier, - TargetNames: []string{tableName.SchemaQualifier.String(), tableName.Name.String()}, + TargetType: auth.AuthTargetType_TableIdentifiers, + TargetNames: []string{tableName.DbQualifier.String(), tableName.SchemaQualifier.String(), tableName.Name.String()}, }, }, nil } diff --git a/server/ast/union_clause.go b/server/ast/union_clause.go index d1e8ec15ee..acad8ed534 100644 --- a/server/ast/union_clause.go +++ b/server/ast/union_clause.go @@ -50,8 +50,11 @@ func nodeUnionClause(ctx *Context, node *tree.UnionClause) (*vitess.SetOp, error unionType = vitess.IntersectStr } case tree.ExceptOp: - // This is not implemented on the GMS side, so we'll throw an appropriate error here - return nil, fmt.Errorf("EXCEPT is not yet supported") + if node.All { + unionType = vitess.ExceptAllStr + } else { + unionType = vitess.ExceptStr + } default: return nil, fmt.Errorf("unknown type of UNION operator: `%s`", node.Type.String()) } diff --git a/server/ast/with.go b/server/ast/with.go index 8b4551d756..9dc4edd6ee 100644 --- a/server/ast/with.go +++ b/server/ast/with.go @@ -64,7 +64,7 @@ func nodeWith(ctx *Context, node *tree.With) (*vitess.With, error) { return nil, nil } - ctes := make([]vitess.TableExpr, len(node.CTEList)) + ctes := make([]*vitess.CommonTableExpr, len(node.CTEList)) for i, cte := range node.CTEList { var err error ctes[i], err = nodeCTE(ctx, cte) diff --git a/server/auth/auth_handler.go b/server/auth/auth_handler.go index a8f364069e..ae3f0c4d8b 100644 --- a/server/auth/auth_handler.go +++ b/server/auth/auth_handler.go @@ -17,6 +17,7 @@ package auth import ( "errors" "fmt" + "strings" "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/go-mysql-server/sql" @@ -100,8 +101,24 @@ func (h *AuthorizationHandler) HandleAuth(ctx *sql.Context, aqs sql.Authorizatio case AuthType_IGNORE: // This means that authorization is being handled elsewhere (such as a child or parent), and should be ignored here return nil + case AuthType_CREATE: + privileges = []Privilege{Privilege_CREATE} case AuthType_DELETE: privileges = []Privilege{Privilege_DELETE} + case AuthType_DROPTABLE: + if len(auth.TargetNames)%3 != 0 { + return fmt.Errorf("table identifiers has an unsupported count: %d", len(auth.TargetNames)) + } + for i := 0; i < len(auth.TargetNames); i += 3 { + // TODO: handle database + if id := HasOwnerAccess(OwnershipKey{ + PrivilegeObject: PrivilegeObject_TABLE, + Schema: auth.TargetNames[i+1], + Name: auth.TargetNames[i+2], + }, state.role.ID()); !id.IsValid() { + return fmt.Errorf("permission denied for table %s", auth.TargetNames[i+2]) + } + } case AuthType_INSERT: privileges = []Privilege{Privilege_INSERT} case AuthType_SELECT: @@ -122,28 +139,73 @@ func (h *AuthorizationHandler) HandleAuth(ctx *sql.Context, aqs sql.Authorizatio switch auth.TargetType { case AuthTargetType_Ignore: // This means that the AuthType did not need a TargetType, so we can safely ignore it - case AuthTargetType_SingleTableIdentifier: - schemaName, err := core.GetSchemaName(ctx, nil, auth.TargetNames[0]) - if err != nil { - return sql.ErrTableNotFound.New(auth.TargetNames[1]) + case AuthTargetType_DatabaseIdentifiers: + for _, database := range auth.TargetNames { + database = h.dbName(ctx, database) + roleDatabaseKey := DatabasePrivilegeKey{ + Role: state.role.ID(), + Name: database, + } + publicDatabaseKey := DatabasePrivilegeKey{ + Role: state.public.ID(), + Name: database, + } + for _, privilege := range privileges { + if !HasDatabasePrivilege(roleDatabaseKey, privilege) && !HasDatabasePrivilege(publicDatabaseKey, privilege) { + return fmt.Errorf("permission denied for database %s", database) + } + } } - ownerKey := OwnershipKey{ - PrivilegeObject: PrivilegeObject_TABLE, - Schema: schemaName, - Name: auth.TargetNames[1], + case AuthTargetType_SchemaIdentifiers: + if len(auth.TargetNames)%2 != 0 { + return fmt.Errorf("schema identifiers has an unsupported count: %d", len(auth.TargetNames)) } - roleTableKey := TablePrivilegeKey{ - Role: state.role.ID(), - Table: doltdb.TableName{Name: auth.TargetNames[1], Schema: schemaName}, + for i := 0; i < len(auth.TargetNames); i += 2 { + // TODO: handle database + schemaName, err := core.GetSchemaName(ctx, nil, auth.TargetNames[i+1]) + if err != nil { + // If this fails, then there's an issue with the search path. + // This will error later in the process, so we'll pass auth for now. + return nil + } + roleSchemaKey := SchemaPrivilegeKey{ + Role: state.role.ID(), + Schema: schemaName, + } + publicSchemaKey := SchemaPrivilegeKey{ + Role: state.public.ID(), + Schema: schemaName, + } + for _, privilege := range privileges { + if !HasSchemaPrivilege(roleSchemaKey, privilege) && !HasSchemaPrivilege(publicSchemaKey, privilege) { + return fmt.Errorf("permission denied for schema %s", schemaName) + } + } } - publicTableKey := TablePrivilegeKey{ - Role: state.public.ID(), - Table: doltdb.TableName{Name: auth.TargetNames[1], Schema: schemaName}, + case AuthTargetType_TableIdentifiers: + if len(auth.TargetNames)%3 != 0 { + return fmt.Errorf("table identifiers has an unsupported count: %d", len(auth.TargetNames)) } - for _, privilege := range privileges { - if !state.role.IsSuperUser && !IsOwner(ownerKey, state.role.ID()) && - !HasTablePrivilege(roleTableKey, privilege) && !HasTablePrivilege(publicTableKey, privilege) { - return fmt.Errorf("permission denied for table %s", auth.TargetNames[1]) + for i := 0; i < len(auth.TargetNames); i += 3 { + // TODO: handle database + schemaName, err := core.GetSchemaName(ctx, nil, auth.TargetNames[i+1]) + if err != nil { + // If this fails, then there's an issue with the search path. + // This will error later in the process, so we'll pass auth for now. + return nil + } + roleTableKey := TablePrivilegeKey{ + Role: state.role.ID(), + Table: doltdb.TableName{Name: auth.TargetNames[i+2], Schema: schemaName}, + } + publicTableKey := TablePrivilegeKey{ + Role: state.public.ID(), + Table: doltdb.TableName{Name: auth.TargetNames[i+2], Schema: schemaName}, + } + for _, privilege := range privileges { + if !HasTablePrivilege(roleTableKey, privilege) && !HasTablePrivilege(publicTableKey, privilege) { + return fmt.Errorf("permission denied for table %s", auth.TargetNames[i+2]) + } } } case AuthTargetType_TODO: @@ -209,3 +271,14 @@ func (h *AuthorizationHandler) CheckTable(ctx *sql.Context, aqs sql.Authorizatio // TODO: implement this return nil } + +// dbName uses the current database from the context if a database is not specified, otherwise it returns the given +// database name. +func (h *AuthorizationHandler) dbName(ctx *sql.Context, dbName string) string { + if len(dbName) == 0 { + dbName = ctx.GetCurrentDatabase() + } + // Revision databases take the form "dbname/revision", so we must split the revision from the database name + splitDbName := strings.SplitN(dbName, "/", 2) + return splitDbName[0] +} diff --git a/server/auth/auth_information.go b/server/auth/auth_information.go index 927f40f9ef..c7a30cc1da 100644 --- a/server/auth/auth_information.go +++ b/server/auth/auth_information.go @@ -21,6 +21,7 @@ const ( AuthType_CONNECT = "CONNECT" AuthType_CREATE = "CREATE" AuthType_DELETE = "DELETE" + AuthType_DROPTABLE = "DROPTABLE" AuthType_EXECUTE = "EXECUTE" AuthType_INSERT = "INSERT" AuthType_REFERENCES = "REFERENCES" @@ -35,11 +36,9 @@ const ( // These AuthTargetType_ enums are used as the TargetType in vitess.AuthInformation. const ( - AuthTargetType_Ignore = "IGNORE" - AuthTargetType_DatabaseIdentifiers = "DB_IDENTS" - AuthTargetType_Global = "GLOBAL" - AuthTargetType_MultipleTableIdentifiers = "DB_TABLE_IDENTS" - AuthTargetType_SingleTableIdentifier = "DB_TABLE_IDENT" - AuthTargetType_TableColumn = "DB_TABLE_COLUMN_IDENT" - AuthTargetType_TODO = "TODO" + AuthTargetType_Ignore = "IGNORE" + AuthTargetType_DatabaseIdentifiers = "DB_IDENTS" + AuthTargetType_SchemaIdentifiers = "DB_SCH_IDENTS" + AuthTargetType_TableIdentifiers = "DB_SCH_TABLE_IDENTS" + AuthTargetType_TODO = "TODO" ) diff --git a/server/auth/database.go b/server/auth/database.go index 3122bb8aaa..858edbfa03 100644 --- a/server/auth/database.go +++ b/server/auth/database.go @@ -36,16 +36,24 @@ var ( // Database contains all information pertaining to authorization and privileges. This is a global structure that is // shared between all branches. type Database struct { - rolesByName map[string]RoleID - rolesByID map[RoleID]Role - ownership *Ownership - tablePrivileges *TablePrivileges + rolesByName map[string]RoleID + rolesByID map[RoleID]Role + ownership *Ownership + databasePrivileges *DatabasePrivileges + schemaPrivileges *SchemaPrivileges + tablePrivileges *TablePrivileges + roleMembership *RoleMembership } // ClearDatabase clears the internal database, leaving only the default users. This is primarily for use by tests. func ClearDatabase() { clear(globalDatabase.rolesByName) clear(globalDatabase.rolesByID) + clear(globalDatabase.ownership.Data) + clear(globalDatabase.databasePrivileges.Data) + clear(globalDatabase.schemaPrivileges.Data) + clear(globalDatabase.tablePrivileges.Data) + clear(globalDatabase.roleMembership.Data) dbInitDefault() } @@ -54,7 +62,7 @@ func DropRole(name string) { if roleID, ok := globalDatabase.rolesByName[name]; ok { delete(globalDatabase.rolesByName, name) delete(globalDatabase.rolesByID, roleID) - + // TODO: remove from ownership, schema privileges, table privileges, and role membership } } @@ -99,6 +107,11 @@ func SetRole(role Role) { globalDatabase.rolesByID[role.ID()] = role } +// IsSuperUser returns whether the given role is a SUPERUSER. +func IsSuperUser(role RoleID) bool { + return globalDatabase.rolesByID[role].IsSuperUser +} + // LockRead takes an anonymous function and runs it while using a read lock. This ensures that the lock is automatically // released once the function finishes. func LockRead(f func()) { @@ -119,10 +132,13 @@ func LockWrite(f func()) { // terribly wrong. func dbInit(dEnv *env.DoltEnv) { globalDatabase = Database{ - rolesByName: make(map[string]RoleID), - rolesByID: make(map[RoleID]Role), - ownership: NewOwnership(), - tablePrivileges: NewTablePrivileges(), + rolesByName: make(map[string]RoleID), + rolesByID: make(map[RoleID]Role), + ownership: NewOwnership(), + databasePrivileges: NewDatabasePrivileges(), + schemaPrivileges: NewSchemaPrivileges(), + tablePrivileges: NewTablePrivileges(), + roleMembership: NewRoleMembership(), } globalLock = &sync.RWMutex{} if dEnv != nil { diff --git a/server/auth/database_privileges.go b/server/auth/database_privileges.go new file mode 100644 index 0000000000..1352d2df62 --- /dev/null +++ b/server/auth/database_privileges.go @@ -0,0 +1,218 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package auth + +import ( + "github.com/dolthub/doltgresql/utils" +) + +// DatabasePrivileges contains the privileges given to a role on a database. +type DatabasePrivileges struct { + Data map[DatabasePrivilegeKey]DatabasePrivilegeValue +} + +// DatabasePrivilegeKey points to a specific database object. +type DatabasePrivilegeKey struct { + Role RoleID + Name string +} + +// DatabasePrivilegeValue is the value associated with the DatabasePrivilegeKey. +type DatabasePrivilegeValue struct { + Key DatabasePrivilegeKey + Privileges map[Privilege]map[GrantedPrivilege]bool +} + +// NewDatabasePrivileges returns a new *DatabasePrivileges. +func NewDatabasePrivileges() *DatabasePrivileges { + return &DatabasePrivileges{make(map[DatabasePrivilegeKey]DatabasePrivilegeValue)} +} + +// AddDatabasePrivilege adds the given database privilege to the global database. +func AddDatabasePrivilege(key DatabasePrivilegeKey, privilege GrantedPrivilege, withGrantOption bool) { + databasePrivilegeValue, ok := globalDatabase.databasePrivileges.Data[key] + if !ok { + databasePrivilegeValue = DatabasePrivilegeValue{ + Key: key, + Privileges: make(map[Privilege]map[GrantedPrivilege]bool), + } + globalDatabase.databasePrivileges.Data[key] = databasePrivilegeValue + } + privilegeMap, ok := databasePrivilegeValue.Privileges[privilege.Privilege] + if !ok { + privilegeMap = make(map[GrantedPrivilege]bool) + databasePrivilegeValue.Privileges[privilege.Privilege] = privilegeMap + } + privilegeMap[privilege] = withGrantOption +} + +// HasDatabasePrivilege checks whether the user has the given privilege on the associated database. +func HasDatabasePrivilege(key DatabasePrivilegeKey, privilege Privilege) bool { + if IsSuperUser(key.Role) || IsOwner(OwnershipKey{ + PrivilegeObject: PrivilegeObject_DATABASE, + Name: key.Name, + }, key.Role) { + return true + } + if databasePrivilegeValue, ok := globalDatabase.databasePrivileges.Data[key]; ok { + if privilegeMap, ok := databasePrivilegeValue.Privileges[privilege]; ok && len(privilegeMap) > 0 { + return true + } + } + for _, group := range GetAllGroupsWithMember(key.Role, true) { + if HasDatabasePrivilege(DatabasePrivilegeKey{ + Role: group, + Name: key.Name, + }, privilege) { + return true + } + } + return false +} + +// HasDatabasePrivilegeGrantOption checks whether the user has WITH GRANT OPTION for the given privilege on the associated +// database. Returns the role that has WITH GRANT OPTION, or an invalid role if WITH GRANT OPTION is not available. +func HasDatabasePrivilegeGrantOption(key DatabasePrivilegeKey, privilege Privilege) RoleID { + ownershipKey := OwnershipKey{ + PrivilegeObject: PrivilegeObject_DATABASE, + Name: key.Name, + } + if IsSuperUser(key.Role) { + owners := GetOwners(ownershipKey) + if len(owners) == 0 { + // This may happen if the privilege file is deleted + return key.Role + } + // Although there may be multiple owners, we'll only return the first one. + // Postgres already allows for non-determinism with multiple membership paths, so this is fine. + return owners[0] + } else if IsOwner(ownershipKey, key.Role) { + return key.Role + } + if databasePrivilegeValue, ok := globalDatabase.databasePrivileges.Data[key]; ok { + if privilegeMap, ok := databasePrivilegeValue.Privileges[privilege]; ok { + for _, withGrantOption := range privilegeMap { + if withGrantOption { + return key.Role + } + } + } + } + for _, group := range GetAllGroupsWithMember(key.Role, true) { + if returnedID := HasDatabasePrivilegeGrantOption(DatabasePrivilegeKey{ + Role: group, + Name: key.Name, + }, privilege); returnedID.IsValid() { + return returnedID + } + } + return 0 +} + +// RemoveDatabasePrivilege removes the privilege from the global database. If `grantOptionOnly` is true, then only the WITH +// GRANT OPTION portion is revoked. If `grantOptionOnly` is false, then the full privilege is removed. If the GrantedBy +// field contains a valid RoleID, then only the privilege associated with that granter is removed. Otherwise, the +// privilege is completely removed for the grantee. +func RemoveDatabasePrivilege(key DatabasePrivilegeKey, privilege GrantedPrivilege, grantOptionOnly bool) { + if databasePrivilegeValue, ok := globalDatabase.databasePrivileges.Data[key]; ok { + if privilegeMap, ok := databasePrivilegeValue.Privileges[privilege.Privilege]; ok { + if grantOptionOnly { + // This is provided when we only want to revoke the WITH GRANT OPTION, and not the privilege itself. + // If a role is provided in GRANTED BY, then we specifically delete the option associated with that role. + // If no role was given, then we'll remove WITH GRANT OPTION from all of the associated roles. + if privilege.GrantedBy.IsValid() { + if _, ok = privilegeMap[privilege]; ok { + privilegeMap[privilege] = false + } + } else { + for privilegeMapKey := range privilegeMap { + privilegeMap[privilegeMapKey] = false + } + } + } else { + // If a role is provided in GRANTED BY, then we specifically delete the privilege associated with that role. + // If no role was given, then we'll delete the privileges granted by all roles. + if privilege.GrantedBy.IsValid() { + delete(privilegeMap, privilege) + } else { + privilegeMap = nil + } + if len(privilegeMap) == 0 { + delete(databasePrivilegeValue.Privileges, privilege.Privilege) + } + } + } + if len(databasePrivilegeValue.Privileges) == 0 { + delete(globalDatabase.databasePrivileges.Data, key) + } + } +} + +// serialize writes the DatabasePrivileges to the given writer. +func (sp *DatabasePrivileges) serialize(writer *utils.Writer) { + // Version 0 + // Write the total number of values + writer.Uint64(uint64(len(sp.Data))) + for _, value := range sp.Data { + // Write the key + writer.Uint64(uint64(value.Key.Role)) + writer.String(value.Key.Name) + // Write the total number of privileges + writer.Uint64(uint64(len(value.Privileges))) + for privilege, privilegeMap := range value.Privileges { + writer.String(string(privilege)) + // Write the number of granted privileges + writer.Uint32(uint32(len(privilegeMap))) + for grantedPrivilege, withGrantOption := range privilegeMap { + writer.Uint64(uint64(grantedPrivilege.GrantedBy)) + writer.Bool(withGrantOption) + } + } + } +} + +// deserialize reads the DatabasePrivileges from the given reader. +func (sp *DatabasePrivileges) deserialize(version uint32, reader *utils.Reader) { + sp.Data = make(map[DatabasePrivilegeKey]DatabasePrivilegeValue) + switch version { + case 0: + // Read the total number of values + dataCount := reader.Uint64() + for dataIdx := uint64(0); dataIdx < dataCount; dataIdx++ { + // Read the key + spv := DatabasePrivilegeValue{Privileges: make(map[Privilege]map[GrantedPrivilege]bool)} + spv.Key.Role = RoleID(reader.Uint64()) + spv.Key.Name = reader.String() + // Read the total number of privileges + privilegeCount := reader.Uint64() + for privilegeIdx := uint64(0); privilegeIdx < privilegeCount; privilegeIdx++ { + privilege := Privilege(reader.String()) + // Read the number of granted privileges + grantedCount := reader.Uint32() + grantedMap := make(map[GrantedPrivilege]bool) + for grantedIdx := uint32(0); grantedIdx < grantedCount; grantedIdx++ { + grantedPrivilege := GrantedPrivilege{} + grantedPrivilege.Privilege = privilege + grantedPrivilege.GrantedBy = RoleID(reader.Uint64()) + grantedMap[grantedPrivilege] = reader.Bool() + } + spv.Privileges[privilege] = grantedMap + } + sp.Data[spv.Key] = spv + } + default: + panic("unexpected version in DatabasePrivileges") + } +} diff --git a/server/auth/ownership.go b/server/auth/ownership.go index 6267640432..479e08c68e 100644 --- a/server/auth/ownership.go +++ b/server/auth/ownership.go @@ -37,6 +37,7 @@ func NewOwnership() *Ownership { // AddOwner adds the given role as an owner to the global database. func AddOwner(key OwnershipKey, role RoleID) { + key = key.normalize() ownerMap, ok := globalDatabase.ownership.Data[key] if !ok { ownerMap = make(map[RoleID]struct{}) @@ -47,14 +48,16 @@ func AddOwner(key OwnershipKey, role RoleID) { // GetOwners returns all owners matching the given key. func GetOwners(key OwnershipKey) []RoleID { + key = key.normalize() if ownerMap, ok := globalDatabase.ownership.Data[key]; ok { return utils.GetMapKeysSorted(ownerMap) } return nil } -// IsOwner returns whether the given owner has an entry for the key. +// IsOwner returns whether the given role is an owner for the key. func IsOwner(key OwnershipKey, role RoleID) bool { + key = key.normalize() if ownerMap, ok := globalDatabase.ownership.Data[key]; ok { _, ok = ownerMap[role] return ok @@ -62,8 +65,33 @@ func IsOwner(key OwnershipKey, role RoleID) bool { return false } +// HasOwnerAccess returns whether the given role has access to the ownership of an object, along with the ID of the true +// owner (which may be the same as the given role). +func HasOwnerAccess(key OwnershipKey, role RoleID) RoleID { + if IsSuperUser(role) { + owners := GetOwners(key) + if len(owners) == 0 { + // This may happen if the privilege file is deleted + return role + } + // Although there may be multiple owners, we'll only return the first one. + // Postgres already allows for non-determinism with multiple membership paths, so this is fine. + return owners[0] + } + if IsOwner(key, role) { + return role + } + for _, group := range GetAllGroupsWithMember(role, true) { + if returnedID := HasOwnerAccess(key, group); returnedID.IsValid() { + return returnedID + } + } + return 0 +} + // RemoveOwner removes the role as an owner from the global database. func RemoveOwner(key OwnershipKey, role RoleID) { + key = key.normalize() if ownerMap, ok := globalDatabase.ownership.Data[key]; ok { delete(ownerMap, role) if len(ownerMap) == 0 { @@ -72,6 +100,26 @@ func RemoveOwner(key OwnershipKey, role RoleID) { } } +// normalize accounts for and corrects any potential variation for specific object types. +func (key OwnershipKey) normalize() OwnershipKey { + if key.PrivilegeObject == PrivilegeObject_SCHEMA { + if len(key.Schema) == 0 { + return OwnershipKey{ + PrivilegeObject: PrivilegeObject_SCHEMA, + Schema: key.Name, + Name: key.Name, + } + } else if len(key.Name) == 0 { + return OwnershipKey{ + PrivilegeObject: PrivilegeObject_SCHEMA, + Schema: key.Schema, + Name: key.Schema, + } + } + } + return key +} + // serialize writes the Ownership to the given writer. func (ownership *Ownership) serialize(writer *utils.Writer) { // Version 0 diff --git a/server/auth/role_membership.go b/server/auth/role_membership.go new file mode 100644 index 0000000000..e4fd627607 --- /dev/null +++ b/server/auth/role_membership.go @@ -0,0 +1,167 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package auth + +import "github.com/dolthub/doltgresql/utils" + +// RoleMembership contains all roles that have been granted to other roles. +type RoleMembership struct { + Data map[RoleID]map[RoleID]RoleMembershipValue +} + +// RoleMembershipValue contains specific membership information between two roles. +type RoleMembershipValue struct { + Member RoleID + Group RoleID + WithAdminOption bool + GrantedBy RoleID +} + +// NewRoleMembership returns a new *RoleMembership. +func NewRoleMembership() *RoleMembership { + return &RoleMembership{ + Data: make(map[RoleID]map[RoleID]RoleMembershipValue), + } +} + +// AddMemberToGroup adds the member role to the group role. +func AddMemberToGroup(member RoleID, group RoleID, withAdminOption bool, grantedBy RoleID) { + // We'll perform a sanity check for circular membership. This should be done before this call is made, but since we + // make assumptions that circular relationships are forbidden (which could lead to infinite loops otherwise), we + // enforce it here too. + if groupID, _, _ := IsRoleAMember(group, member); (groupID.IsValid() || member == group) && !globalDatabase.rolesByID[group].IsSuperUser { + panic("missing validation to prevent circular role relationships") + } + groupMap, ok := globalDatabase.roleMembership.Data[member] + if !ok { + groupMap = make(map[RoleID]RoleMembershipValue) + globalDatabase.roleMembership.Data[member] = groupMap + } + groupMap[group] = RoleMembershipValue{ + Member: member, + Group: group, + WithAdminOption: withAdminOption, + GrantedBy: grantedBy, + } +} + +// IsRoleAMember returns whether the given role is a member of the group by returning the group's ID. Also returns +// whether the member was granted WITH ADMIN OPTION, allowing it to grant membership to the group to other roles. A +// member does not automatically have ADMIN OPTION on itself, therefore this check must be performed. +func IsRoleAMember(member RoleID, group RoleID) (groupID RoleID, inheritsPrivileges bool, hasWithAdminOption bool) { + // If the member and group are the same, then we only check for SUPERUSER status to allow WITH ADMIN OPTION + if member == group { + return group, true, globalDatabase.rolesByID[member].IsSuperUser + } + // Postgres does not allow for circular role membership, so we can recursively check without worry: + // https://www.postgresql.org/docs/15/catalog-pg-auth-members.html + if groupMap, ok := globalDatabase.roleMembership.Data[member]; ok { + for _, value := range groupMap { + if value.Group == group { + return group, globalDatabase.rolesByID[member].InheritPrivileges, value.WithAdminOption + } + // This recursively walks through memberships + if groupID, _, hasWithAdminOption = IsRoleAMember(value.Group, group); groupID.IsValid() { + return groupID, globalDatabase.rolesByID[member].InheritPrivileges, hasWithAdminOption + } + } + } + // A SUPERUSER has access to everything, and therefore functions as though it's a member of every group + if globalDatabase.rolesByID[member].IsSuperUser { + return group, true, true + } + return 0, false, false +} + +// GetAllGroupsWithMember returns every group that the role is a direct member of. This can also filter by groups that +// the member has privilege access on. +func GetAllGroupsWithMember(member RoleID, inheritsPrivilegesOnly bool) []RoleID { + memberRole, ok := globalDatabase.rolesByID[member] + if !ok || !memberRole.InheritPrivileges { + return nil + } + groupMap := globalDatabase.roleMembership.Data[member] + groups := make([]RoleID, 0, len(groupMap)) + for groupID := range groupMap { + groups = append(groups, groupID) + } + return groups +} + +// RemoveMemberFromGroup removes the member from the group. If `adminOptionOnly` is true, then only the WITH ADMIN +// OPTION portion is revoked. If `adminOptionOnly` is false, then the member is fully is removed. +func RemoveMemberFromGroup(member RoleID, group RoleID, adminOptionOnly bool) { + if groupMap, ok := globalDatabase.roleMembership.Data[member]; ok { + if adminOptionOnly { + value := groupMap[group] + value.WithAdminOption = false + groupMap[group] = value + } else { + delete(groupMap, group) + } + if len(groupMap) == 0 { + delete(globalDatabase.roleMembership.Data, member) + } + } +} + +// serialize writes the RoleMembership to the given writer. +func (membership *RoleMembership) serialize(writer *utils.Writer) { + // Version 0 + // Write the total number of members + writer.Uint64(uint64(len(membership.Data))) + for _, groupMap := range membership.Data { + // Write the number of groups + writer.Uint64(uint64(len(groupMap))) + for _, mapValue := range groupMap { + // Write the membership information + writer.Uint64(uint64(mapValue.Member)) + writer.Uint64(uint64(mapValue.Group)) + writer.Bool(mapValue.WithAdminOption) + writer.Uint64(uint64(mapValue.GrantedBy)) + } + } +} + +// deserialize reads the RoleMembership from the given reader. +func (membership *RoleMembership) deserialize(version uint32, reader *utils.Reader) { + membership.Data = make(map[RoleID]map[RoleID]RoleMembershipValue) + switch version { + case 0: + // Read the total number of members + memberCount := reader.Uint64() + for memberIdx := uint64(0); memberIdx < memberCount; memberIdx++ { + // Read the number of groups + groupCount := reader.Uint64() + groupMap := make(map[RoleID]RoleMembershipValue) + var member RoleID + for groupIdx := uint64(0); groupIdx < groupCount; groupIdx++ { + // Read the membership information + value := RoleMembershipValue{} + value.Member = RoleID(reader.Uint64()) + value.Group = RoleID(reader.Uint64()) + value.WithAdminOption = reader.Bool() + value.GrantedBy = RoleID(reader.Uint64()) + // Add the information to the map + groupMap[value.Group] = value + member = value.Member + } + // Add the group map to the data + membership.Data[member] = groupMap + } + default: + panic("unexpected version in RoleMembership") + } +} diff --git a/server/auth/schema_privileges.go b/server/auth/schema_privileges.go new file mode 100644 index 0000000000..40a7f501c2 --- /dev/null +++ b/server/auth/schema_privileges.go @@ -0,0 +1,218 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package auth + +import ( + "github.com/dolthub/doltgresql/utils" +) + +// SchemaPrivileges contains the privileges given to a role on a schema. +type SchemaPrivileges struct { + Data map[SchemaPrivilegeKey]SchemaPrivilegeValue +} + +// SchemaPrivilegeKey points to a specific schema object. +type SchemaPrivilegeKey struct { + Role RoleID + Schema string +} + +// SchemaPrivilegeValue is the value associated with the SchemaPrivilegeKey. +type SchemaPrivilegeValue struct { + Key SchemaPrivilegeKey + Privileges map[Privilege]map[GrantedPrivilege]bool +} + +// NewSchemaPrivileges returns a new *SchemaPrivileges. +func NewSchemaPrivileges() *SchemaPrivileges { + return &SchemaPrivileges{make(map[SchemaPrivilegeKey]SchemaPrivilegeValue)} +} + +// AddSchemaPrivilege adds the given schema privilege to the global database. +func AddSchemaPrivilege(key SchemaPrivilegeKey, privilege GrantedPrivilege, withGrantOption bool) { + schemaPrivilegeValue, ok := globalDatabase.schemaPrivileges.Data[key] + if !ok { + schemaPrivilegeValue = SchemaPrivilegeValue{ + Key: key, + Privileges: make(map[Privilege]map[GrantedPrivilege]bool), + } + globalDatabase.schemaPrivileges.Data[key] = schemaPrivilegeValue + } + privilegeMap, ok := schemaPrivilegeValue.Privileges[privilege.Privilege] + if !ok { + privilegeMap = make(map[GrantedPrivilege]bool) + schemaPrivilegeValue.Privileges[privilege.Privilege] = privilegeMap + } + privilegeMap[privilege] = withGrantOption +} + +// HasSchemaPrivilege checks whether the user has the given privilege on the associated schema. +func HasSchemaPrivilege(key SchemaPrivilegeKey, privilege Privilege) bool { + if IsSuperUser(key.Role) || IsOwner(OwnershipKey{ + PrivilegeObject: PrivilegeObject_SCHEMA, + Schema: key.Schema, + }, key.Role) { + return true + } + if schemaPrivilegeValue, ok := globalDatabase.schemaPrivileges.Data[key]; ok { + if privilegeMap, ok := schemaPrivilegeValue.Privileges[privilege]; ok && len(privilegeMap) > 0 { + return true + } + } + for _, group := range GetAllGroupsWithMember(key.Role, true) { + if HasSchemaPrivilege(SchemaPrivilegeKey{ + Role: group, + Schema: key.Schema, + }, privilege) { + return true + } + } + return false +} + +// HasSchemaPrivilegeGrantOption checks whether the user has WITH GRANT OPTION for the given privilege on the associated +// schema. Returns the role that has WITH GRANT OPTION, or an invalid role if WITH GRANT OPTION is not available. +func HasSchemaPrivilegeGrantOption(key SchemaPrivilegeKey, privilege Privilege) RoleID { + ownershipKey := OwnershipKey{ + PrivilegeObject: PrivilegeObject_SCHEMA, + Schema: key.Schema, + } + if IsSuperUser(key.Role) { + owners := GetOwners(ownershipKey) + if len(owners) == 0 { + // This may happen if the privilege file is deleted + return key.Role + } + // Although there may be multiple owners, we'll only return the first one. + // Postgres already allows for non-determinism with multiple membership paths, so this is fine. + return owners[0] + } else if IsOwner(ownershipKey, key.Role) { + return key.Role + } + if schemaPrivilegeValue, ok := globalDatabase.schemaPrivileges.Data[key]; ok { + if privilegeMap, ok := schemaPrivilegeValue.Privileges[privilege]; ok { + for _, withGrantOption := range privilegeMap { + if withGrantOption { + return key.Role + } + } + } + } + for _, group := range GetAllGroupsWithMember(key.Role, true) { + if returnedID := HasSchemaPrivilegeGrantOption(SchemaPrivilegeKey{ + Role: group, + Schema: key.Schema, + }, privilege); returnedID.IsValid() { + return returnedID + } + } + return 0 +} + +// RemoveSchemaPrivilege removes the privilege from the global database. If `grantOptionOnly` is true, then only the WITH +// GRANT OPTION portion is revoked. If `grantOptionOnly` is false, then the full privilege is removed. If the GrantedBy +// field contains a valid RoleID, then only the privilege associated with that granter is removed. Otherwise, the +// privilege is completely removed for the grantee. +func RemoveSchemaPrivilege(key SchemaPrivilegeKey, privilege GrantedPrivilege, grantOptionOnly bool) { + if schemaPrivilegeValue, ok := globalDatabase.schemaPrivileges.Data[key]; ok { + if privilegeMap, ok := schemaPrivilegeValue.Privileges[privilege.Privilege]; ok { + if grantOptionOnly { + // This is provided when we only want to revoke the WITH GRANT OPTION, and not the privilege itself. + // If a role is provided in GRANTED BY, then we specifically delete the option associated with that role. + // If no role was given, then we'll remove WITH GRANT OPTION from all of the associated roles. + if privilege.GrantedBy.IsValid() { + if _, ok = privilegeMap[privilege]; ok { + privilegeMap[privilege] = false + } + } else { + for privilegeMapKey := range privilegeMap { + privilegeMap[privilegeMapKey] = false + } + } + } else { + // If a role is provided in GRANTED BY, then we specifically delete the privilege associated with that role. + // If no role was given, then we'll delete the privileges granted by all roles. + if privilege.GrantedBy.IsValid() { + delete(privilegeMap, privilege) + } else { + privilegeMap = nil + } + if len(privilegeMap) == 0 { + delete(schemaPrivilegeValue.Privileges, privilege.Privilege) + } + } + } + if len(schemaPrivilegeValue.Privileges) == 0 { + delete(globalDatabase.schemaPrivileges.Data, key) + } + } +} + +// serialize writes the SchemaPrivileges to the given writer. +func (sp *SchemaPrivileges) serialize(writer *utils.Writer) { + // Version 0 + // Write the total number of values + writer.Uint64(uint64(len(sp.Data))) + for _, value := range sp.Data { + // Write the key + writer.Uint64(uint64(value.Key.Role)) + writer.String(value.Key.Schema) + // Write the total number of privileges + writer.Uint64(uint64(len(value.Privileges))) + for privilege, privilegeMap := range value.Privileges { + writer.String(string(privilege)) + // Write the number of granted privileges + writer.Uint32(uint32(len(privilegeMap))) + for grantedPrivilege, withGrantOption := range privilegeMap { + writer.Uint64(uint64(grantedPrivilege.GrantedBy)) + writer.Bool(withGrantOption) + } + } + } +} + +// deserialize reads the SchemaPrivileges from the given reader. +func (sp *SchemaPrivileges) deserialize(version uint32, reader *utils.Reader) { + sp.Data = make(map[SchemaPrivilegeKey]SchemaPrivilegeValue) + switch version { + case 0: + // Read the total number of values + dataCount := reader.Uint64() + for dataIdx := uint64(0); dataIdx < dataCount; dataIdx++ { + // Read the key + spv := SchemaPrivilegeValue{Privileges: make(map[Privilege]map[GrantedPrivilege]bool)} + spv.Key.Role = RoleID(reader.Uint64()) + spv.Key.Schema = reader.String() + // Read the total number of privileges + privilegeCount := reader.Uint64() + for privilegeIdx := uint64(0); privilegeIdx < privilegeCount; privilegeIdx++ { + privilege := Privilege(reader.String()) + // Read the number of granted privileges + grantedCount := reader.Uint32() + grantedMap := make(map[GrantedPrivilege]bool) + for grantedIdx := uint32(0); grantedIdx < grantedCount; grantedIdx++ { + grantedPrivilege := GrantedPrivilege{} + grantedPrivilege.Privilege = privilege + grantedPrivilege.GrantedBy = RoleID(reader.Uint64()) + grantedMap[grantedPrivilege] = reader.Bool() + } + spv.Privileges[privilege] = grantedMap + } + sp.Data[spv.Key] = spv + } + default: + panic("unexpected version in SchemaPrivileges") + } +} diff --git a/server/auth/serialization.go b/server/auth/serialization.go index 2d86724fce..f00dc62e1f 100644 --- a/server/auth/serialization.go +++ b/server/auth/serialization.go @@ -42,8 +42,14 @@ func (db *Database) serialize() []byte { } // Write the ownership db.ownership.serialize(writer) + // Write the database privileges + db.databasePrivileges.serialize(writer) + // Write the schema privileges + db.schemaPrivileges.serialize(writer) // Write the table privileges db.tablePrivileges.serialize(writer) + // Write the role chain + db.roleMembership.serialize(writer) return writer.Data() } @@ -76,7 +82,13 @@ func (db *Database) deserializeV0(reader *utils.Reader) error { } // Read the ownership db.ownership.deserialize(0, reader) + // Read the database privileges + db.databasePrivileges.deserialize(0, reader) + // Read the schema privileges + db.schemaPrivileges.deserialize(0, reader) // Read the table privileges db.tablePrivileges.deserialize(0, reader) + // Read the role chain + db.roleMembership.deserialize(0, reader) return nil } diff --git a/server/auth/table_privileges.go b/server/auth/table_privileges.go index 63a4fd9378..224adb124f 100644 --- a/server/auth/table_privileges.go +++ b/server/auth/table_privileges.go @@ -62,27 +62,87 @@ func AddTablePrivilege(key TablePrivilegeKey, privilege GrantedPrivilege, withGr // HasTablePrivilege checks whether the user has the given privilege on the associated table. func HasTablePrivilege(key TablePrivilegeKey, privilege Privilege) bool { + if IsSuperUser(key.Role) || IsOwner(OwnershipKey{ + PrivilegeObject: PrivilegeObject_TABLE, + Schema: key.Table.Schema, + Name: key.Table.Name, + }, key.Role) { + return true + } + // If a table name was provided, then we also want to search for privileges provided to all tables in the schema + // space. Since those are saved with an empty table name, we can easily do another search by removing the table. + if len(key.Table.Name) > 0 { + if ok := HasTablePrivilege(TablePrivilegeKey{ + Role: key.Role, + Table: doltdb.TableName{Name: "", Schema: key.Table.Schema}, + }, privilege); ok { + return true + } + } if tablePrivilegeValue, ok := globalDatabase.tablePrivileges.Data[key]; ok { - if privilegeMap, ok := tablePrivilegeValue.Privileges[privilege]; ok { - return len(privilegeMap) > 0 + if privilegeMap, ok := tablePrivilegeValue.Privileges[privilege]; ok && len(privilegeMap) > 0 { + return true + } + } + for _, group := range GetAllGroupsWithMember(key.Role, true) { + if HasTablePrivilege(TablePrivilegeKey{ + Role: group, + Table: key.Table, + }, privilege) { + return true } } return false } // HasTablePrivilegeGrantOption checks whether the user has WITH GRANT OPTION for the given privilege on the associated -// table. -func HasTablePrivilegeGrantOption(key TablePrivilegeKey, privilege Privilege) bool { +// table. Returns the role that has WITH GRANT OPTION, or an invalid role if WITH GRANT OPTION is not available. +func HasTablePrivilegeGrantOption(key TablePrivilegeKey, privilege Privilege) RoleID { + ownershipKey := OwnershipKey{ + PrivilegeObject: PrivilegeObject_TABLE, + Schema: key.Table.Schema, + Name: key.Table.Name, + } + if IsSuperUser(key.Role) { + owners := GetOwners(ownershipKey) + if len(owners) == 0 { + // This may happen if the privilege file is deleted + return key.Role + } + // Although there may be multiple owners, we'll only return the first one. + // Postgres already allows for non-determinism with multiple membership paths, so this is fine. + return owners[0] + } else if IsOwner(ownershipKey, key.Role) { + return key.Role + } + // If a table name was provided, then we also want to search for privileges provided to all tables in the schema + // space. Since those are saved with an empty table name, we can easily do another search by removing the table. + if len(key.Table.Name) > 0 { + if returnedID := HasTablePrivilegeGrantOption(TablePrivilegeKey{ + Role: key.Role, + Table: doltdb.TableName{Name: "", Schema: key.Table.Schema}, + }, privilege); returnedID.IsValid() { + return returnedID + } + } if tablePrivilegeValue, ok := globalDatabase.tablePrivileges.Data[key]; ok { if privilegeMap, ok := tablePrivilegeValue.Privileges[privilege]; ok { for _, withGrantOption := range privilegeMap { if withGrantOption { - return true + return key.Role } } } } - return false + for _, group := range GetAllGroupsWithMember(key.Role, true) { + if returnedID := HasTablePrivilegeGrantOption(TablePrivilegeKey{ + Role: group, + Table: key.Table, + }, privilege); returnedID.IsValid() { + return returnedID + } + } + return 0 } // RemoveTablePrivilege removes the privilege from the global database. If `grantOptionOnly` is true, then only the WITH diff --git a/server/functions/init.go b/server/functions/init.go index ab254eceea..184dfc2880 100644 --- a/server/functions/init.go +++ b/server/functions/init.go @@ -121,6 +121,7 @@ func Init() { initTand() initTanh() initTimezone() + initToChar() initToHex() initToRegclass() initToRegproc() diff --git a/server/functions/to_char.go b/server/functions/to_char.go new file mode 100644 index 0000000000..cfa572230b --- /dev/null +++ b/server/functions/to_char.go @@ -0,0 +1,242 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package functions + +import ( + "fmt" + "strings" + "time" + + "github.com/dolthub/go-mysql-server/sql" + + "github.com/dolthub/doltgresql/server/functions/framework" + pgtypes "github.com/dolthub/doltgresql/server/types" +) + +// initToChar registers the functions to the catalog. +func initToChar() { + framework.RegisterFunction(to_char_timestamp) +} + +// to_char_timestamp represents the PostgreSQL function of the same name, taking the same parameters. +// Postgres date formatting: https://www.postgresql.org/docs/8.1/functions-formatting.html +var to_char_timestamp = framework.Function2{ + Name: "to_char", + Return: pgtypes.Text, + Parameters: [2]pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Text}, + Strict: true, + Callable: func(ctx *sql.Context, _ [3]pgtypes.DoltgresType, val1, val2 any) (any, error) { + timestamp := val1.(time.Time) + format := val2.(string) + + year := timestamp.Format("2006") + + result := "" + for len(format) > 0 { + switch { + case strings.HasPrefix(format, "hh24") || strings.HasPrefix(format, "HH24"): + result += timestamp.Format("15") + format = format[4:] + case strings.HasPrefix(format, "hh12") || strings.HasPrefix(format, "HH12"): + result += timestamp.Format("03") + format = format[4:] + case strings.HasPrefix(format, "hh") || strings.HasPrefix(format, "HH"): + result += timestamp.Format("03") + format = format[2:] + + case strings.HasPrefix(format, "mi") || strings.HasPrefix(format, "MI"): + result += timestamp.Format("04") + format = format[2:] + + case strings.HasPrefix(format, "ssss") || strings.HasPrefix(format, "SSSS") || strings.HasPrefix(format, "sssss") || strings.HasPrefix(format, "SSSSS"): + return nil, fmt.Errorf("seconds past midnight not supported") + + case strings.HasPrefix(format, "ss") || strings.HasPrefix(format, "SS"): + result += timestamp.Format("05") + format = format[2:] + + case strings.HasPrefix(format, "ms") || strings.HasPrefix(format, "MS"): + result += fmt.Sprintf("%03d", timestamp.Nanosecond()/1_000_000) + format = format[2:] + + case strings.HasPrefix(format, "us") || strings.HasPrefix(format, "US"): + result += fmt.Sprintf("%06d", timestamp.Nanosecond()/1_000) + format = format[2:] + + case strings.HasPrefix(format, "am") || strings.HasPrefix(format, "pm"): + if timestamp.Hour() < 12 { + result += "am" + } else { + result += "pm" + } + format = format[2:] + case strings.HasPrefix(format, "AM") || strings.HasPrefix(format, "PM"): + if timestamp.Hour() < 12 { + result += "AM" + } else { + result += "PM" + } + format = format[2:] + case strings.HasPrefix(format, "a.m.") || strings.HasPrefix(format, "p.m."): + if timestamp.Hour() < 12 { + result += "a.m." + } else { + result += "p.m." + } + format = format[4:] + case strings.HasPrefix(format, "A.M.") || strings.HasPrefix(format, "P.M."): + if timestamp.Hour() < 12 { + result += "A.M." + } else { + result += "P.M." + } + format = format[4:] + + case strings.HasPrefix(format, "y,yyy") || strings.HasPrefix(format, "Y,YYY"): + result += string(year[0]) + "," + year[1:] + format = format[5:] + case strings.HasPrefix(format, "yyyy") || strings.HasPrefix(format, "YYYY"): + result += year + format = format[4:] + case strings.HasPrefix(format, "yyy") || strings.HasPrefix(format, "YYY"): + result += year[1:] + format = format[3:] + case strings.HasPrefix(format, "yy") || strings.HasPrefix(format, "YY"): + result += year[2:] + format = format[2:] + case strings.HasPrefix(format, "y") || strings.HasPrefix(format, "Y"): + result += year[3:] + format = format[1:] + + case strings.HasPrefix(format, "iyyy") || strings.HasPrefix(format, "IYYY"): + return nil, fmt.Errorf("ISO year not supported") + case strings.HasPrefix(format, "iyy") || strings.HasPrefix(format, "IYY"): + return nil, fmt.Errorf("ISO year not supported") + case strings.HasPrefix(format, "iy") || strings.HasPrefix(format, "IY"): + return nil, fmt.Errorf("ISO year not supported") + + case strings.HasPrefix(format, "bc") || strings.HasPrefix(format, "ad"): + return nil, fmt.Errorf("era indicator not supported") + case strings.HasPrefix(format, "BC") || strings.HasPrefix(format, "AD"): + return nil, fmt.Errorf("era indicator not supported") + case strings.HasPrefix(format, "b.c.") || strings.HasPrefix(format, "a.d."): + return nil, fmt.Errorf("era indicator not supported") + case strings.HasPrefix(format, "B.C.") || strings.HasPrefix(format, "A.D."): + return nil, fmt.Errorf("era indicator not supported") + + case strings.HasPrefix(format, "MONTH"): + result += strings.ToUpper(timestamp.Format("January")) + format = format[5:] + case strings.HasPrefix(format, "Month"): + result += timestamp.Format("January") + format = format[5:] + case strings.HasPrefix(format, "month"): + result += strings.ToLower(timestamp.Format("January")) + format = format[5:] + + case strings.HasPrefix(format, "MON"): + result += strings.ToUpper(timestamp.Format("Jan")) + format = format[3:] + case strings.HasPrefix(format, "Mon"): + result += timestamp.Format("Jan") + format = format[3:] + case strings.HasPrefix(format, "mon"): + result += strings.ToLower(timestamp.Format("Jan")) + format = format[3:] + + case strings.HasPrefix(format, "mm") || strings.HasPrefix(format, "MM"): + result += timestamp.Format("01") + format = format[2:] + + case strings.HasPrefix(format, "DAY"): + result += strings.ToUpper(timestamp.Format("Monday")) + format = format[3:] + case strings.HasPrefix(format, "Day"): + result += timestamp.Format("Monday") + format = format[3:] + case strings.HasPrefix(format, "day"): + result += strings.ToLower(timestamp.Format("Monday")) + format = format[3:] + + case strings.HasPrefix(format, "DY"): + result += strings.ToUpper(timestamp.Format("Mon")) + format = format[2:] + case strings.HasPrefix(format, "Dy"): + result += timestamp.Format("Mon") + format = format[2:] + case strings.HasPrefix(format, "dy"): + result += strings.ToLower(timestamp.Format("Mon")) + format = format[2:] + + case strings.HasPrefix(format, "ddd") || strings.HasPrefix(format, "DDD"): + result += timestamp.Format("002") + format = format[3:] + + case strings.HasPrefix(format, "dd") || strings.HasPrefix(format, "DD"): + result += timestamp.Format("02") + format = format[2:] + + case strings.HasPrefix(format, "d") || strings.HasPrefix(format, "D"): + result += fmt.Sprintf("%d", timestamp.Weekday()+1) + format = format[1:] + + case strings.HasPrefix(format, "ww") || strings.HasPrefix(format, "WW"): + return nil, fmt.Errorf("week of year not supported") + + case strings.HasPrefix(format, "iw") || strings.HasPrefix(format, "IW"): + _, week := timestamp.ISOWeek() + result += fmt.Sprintf("%02d", week) + format = format[2:] + + case strings.HasPrefix(format, "i") || strings.HasPrefix(format, "I"): + return nil, fmt.Errorf("ISO year not supported") + + case strings.HasPrefix(format, "w") || strings.HasPrefix(format, "W"): + return nil, fmt.Errorf("week of month not supported") + + case strings.HasPrefix(format, "cc") || strings.HasPrefix(format, "CC"): + return nil, fmt.Errorf("century not supported") + + case strings.HasPrefix(format, "j") || strings.HasPrefix(format, "J"): + return nil, fmt.Errorf("julian days not supported") + + case strings.HasPrefix(format, "q") || strings.HasPrefix(format, "Q"): + switch timestamp.Month() { + case time.January, time.February, time.March: + result += "1" + case time.April, time.May, time.June: + result += "2" + case time.July, time.August, time.September: + result += "3" + case time.October, time.November, time.December: + result += "4" + } + format = format[1:] + + case strings.HasPrefix(format, "rm") || strings.HasPrefix(format, "RM"): + return nil, fmt.Errorf("roman numeral month not supported") + + case strings.HasPrefix(format, "tz") || strings.HasPrefix(format, "TZ"): + return nil, fmt.Errorf("time-zone name not supported") + + default: + result += string(format[0]) + format = format[1:] + } + } + + return result, nil + }, +} diff --git a/server/node/alter_role.go b/server/node/alter_role.go index cce0ec279b..318e109527 100644 --- a/server/node/alter_role.go +++ b/server/node/alter_role.go @@ -56,22 +56,35 @@ func (c *AlterRole) Resolved() bool { // RowIter implements the interface sql.ExecSourceRel. func (c *AlterRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { + var userRole auth.Role var role auth.Role - var err error auth.LockRead(func() { - if !auth.RoleExists(c.Name) { - err = fmt.Errorf(`role "%s" does not exist`, c.Name) - } else { - role = auth.GetRole(c.Name) - } + userRole = auth.GetRole(ctx.Client().User) + role = auth.GetRole(c.Name) }) - if err != nil { - return nil, err + if !userRole.IsValid() { + return nil, fmt.Errorf(`role "%s" does not exist`, userRole.Name) + } + if !role.IsValid() { + return nil, fmt.Errorf(`role "%s" does not exist`, c.Name) } + if role.IsSuperUser && !userRole.IsSuperUser { + // Only superusers can modify other superusers + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } else if !userRole.IsSuperUser && !userRole.CanCreateRoles && role.ID() != userRole.ID() { + // A role may only modify itself if it doesn't have the ability to create roles + // TODO: allow non-role-creating roles to only modify their own password, and grab actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } for optionName, optionValue := range c.Options { switch optionName { case "BYPASSRLS": + if !userRole.IsSuperUser { + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } role.CanBypassRowLevelSecurity = true case "CONNECTION_LIMIT": role.ConnectionLimit = optionValue.(int32) @@ -84,6 +97,10 @@ func (c *AlterRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { case "LOGIN": role.CanLogin = true case "NOBYPASSRLS": + if !userRole.IsSuperUser { + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } role.CanBypassRowLevelSecurity = false case "NOCREATEDB": role.CanCreateDB = false @@ -94,8 +111,16 @@ func (c *AlterRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { case "NOLOGIN": role.CanLogin = false case "NOREPLICATION": + if !userRole.IsSuperUser { + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } role.IsReplicationRole = false case "NOSUPERUSER": + if !userRole.IsSuperUser { + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } role.IsSuperUser = false case "PASSWORD": password, _ := optionValue.(*string) @@ -109,8 +134,16 @@ func (c *AlterRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { } } case "REPLICATION": + if !userRole.IsSuperUser { + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } role.IsReplicationRole = true case "SUPERUSER": + if !userRole.IsSuperUser { + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to alter role "%s"`, userRole.Name, role.Name) + } role.IsSuperUser = true case "VALID_UNTIL": timeString, _ := optionValue.(*string) @@ -128,6 +161,7 @@ func (c *AlterRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { return nil, fmt.Errorf(`unknown role option "%s"`, optionName) } } + var err error auth.LockWrite(func() { auth.SetRole(role) err = auth.PersistChanges() diff --git a/server/node/create_role.go b/server/node/create_role.go index 64b7662737..c66eaad453 100644 --- a/server/node/create_role.go +++ b/server/node/create_role.go @@ -68,10 +68,15 @@ func (c *CreateRole) Resolved() bool { // RowIter implements the interface sql.ExecSourceRel. func (c *CreateRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { + var userRole auth.Role var roleExists bool auth.LockRead(func() { roleExists = auth.RoleExists(c.Name) + userRole = auth.GetRole(ctx.Client().User) }) + if !userRole.IsValid() { + return nil, fmt.Errorf(`role "%s" does not exist`, ctx.Client().User) + } if roleExists { if c.IfNotExists { return sql.RowsToRowIter(), nil @@ -79,6 +84,10 @@ func (c *CreateRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { return nil, fmt.Errorf(`role "%s" already exists`, c.Name) } + if !userRole.IsSuperUser && (!userRole.CanCreateRoles || c.IsSuperUser) { + // TODO: grab the actual error message + return nil, fmt.Errorf(`role "%s" does not have permission to create the role`, userRole.Name) + } var role auth.Role auth.LockWrite(func() { role = auth.CreateDefaultRole(c.Name) diff --git a/server/node/drop_role.go b/server/node/drop_role.go index 3c9fa9bb8e..56e33775d9 100644 --- a/server/node/drop_role.go +++ b/server/node/drop_role.go @@ -52,13 +52,24 @@ func (c *DropRole) Resolved() bool { func (c *DropRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { // TODO: disallow dropping the role if it owns anything // First we'll loop over all of the names to check that they all exist + var userRole auth.Role + var roles []auth.Role var err error auth.LockRead(func() { + userRole = auth.GetRole(ctx.Client().User) for _, roleName := range c.Names { - if !auth.RoleExists(roleName) && !c.IfExists { + role := auth.GetRole(roleName) + if role.IsValid() { + roles = append(roles, role) + } else if !c.IfExists { err = fmt.Errorf(`role "%s" does not exist`, roleName) break } + if !userRole.IsSuperUser && (role.IsSuperUser || !userRole.CanCreateRoles) { + // TODO: grab the actual error message + err = fmt.Errorf(`role "%s" does not have permission to drop role "%s"`, userRole.Name, role.Name) + break + } } }) if err != nil { @@ -66,8 +77,8 @@ func (c *DropRole) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { } // Then we'll loop again, dropping all of the users auth.LockWrite(func() { - for _, roleName := range c.Names { - auth.DropRole(roleName) + for _, role := range roles { + auth.DropRole(role.Name) } err = auth.PersistChanges() }) diff --git a/server/node/grant.go b/server/node/grant.go index 47ae5480da..f299d71eee 100644 --- a/server/node/grant.go +++ b/server/node/grant.go @@ -30,26 +30,40 @@ import ( // Grant handles all of the GRANT statements. type Grant struct { GrantTable *GrantTable + GrantSchema *GrantSchema + GrantDatabase *GrantDatabase + GrantRole *GrantRole ToRoles []string - WithGrantOption bool // Does not apply to the GRANT TO statement + WithGrantOption bool // This is "WITH ADMIN OPTION" for GrantRole only GrantedBy string } // GrantTable specifically handles the GRANT ... ON TABLE statement. type GrantTable struct { - Privileges []auth.Privilege - Tables []doltdb.TableName - AllTablesInSchemas []string + Privileges []auth.Privilege + Tables []doltdb.TableName } -var _ sql.ExecSourceRel = (*Grant)(nil) -var _ vitess.Injectable = (*Grant)(nil) +// GrantSchema specifically handles the GRANT ... ON SCHEMA statement. +type GrantSchema struct { + Privileges []auth.Privilege + Schemas []string +} -// CheckPrivileges implements the interface sql.ExecSourceRel. -func (g *Grant) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { - return true +// GrantDatabase specifically handles the GRANT ... ON DATABASE statement. +type GrantDatabase struct { + Privileges []auth.Privilege + Databases []string } +// GrantRole specifically handles the GRANT TO statement. +type GrantRole struct { + Groups []string +} + +var _ sql.ExecSourceRel = (*Grant)(nil) +var _ vitess.Injectable = (*Grant)(nil) + // Children implements the interface sql.ExecSourceRel. func (g *Grant) Children() []sql.Node { return nil @@ -71,71 +85,20 @@ func (g *Grant) RowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) { auth.LockWrite(func() { switch { case g.GrantTable != nil: - if len(g.GrantTable.AllTablesInSchemas) > 0 { - err = fmt.Errorf("granting privileges to all tables in the schema is not yet supported") + if err = g.grantTable(ctx); err != nil { return } - roles := make([]auth.Role, len(g.ToRoles)) - // First we'll verify that all of the roles exist - for i, roleName := range g.ToRoles { - roles[i] = auth.GetRole(roleName) - if !roles[i].IsValid() { - err = fmt.Errorf(`role "%s" does not exist`, roleName) - return - } - } - // Then we'll check that the role that is granting the privileges exists - userRole := auth.GetRole(ctx.Client().User) - if !userRole.IsValid() { - err = fmt.Errorf(`role "%s" does not exist`, ctx.Client().User) + case g.GrantSchema != nil: + if err = g.grantSchema(ctx); err != nil { return } - var grantedByID auth.RoleID - if len(g.GrantedBy) != 0 { - // TODO: check the role chain to see if this session's user can assume this role - grantedByRole := auth.GetRole(g.GrantedBy) - if !grantedByRole.IsValid() { - err = fmt.Errorf(`role "%s" does not exist`, g.GrantedBy) - return - } - grantedByID = grantedByRole.ID() - // TODO: check if owners may arbitrarily set the GRANTED BY - if !userRole.IsSuperUser { - err = errors.New("REVOKE currently only allows superusers to set GRANTED BY") - return - } - } else { - grantedByID = userRole.ID() + case g.GrantDatabase != nil: + if err = g.grantDatabase(ctx); err != nil { + return } - // Next we'll assign all of the privileges to each role - for _, role := range roles { - for _, table := range g.GrantTable.Tables { - var schemaName string - schemaName, err = core.GetSchemaName(ctx, nil, table.Schema) - if err != nil { - return - } - key := auth.TablePrivilegeKey{ - Role: role.ID(), - Table: doltdb.TableName{Name: table.Name, Schema: schemaName}, - } - isOwner := auth.IsOwner(auth.OwnershipKey{ - PrivilegeObject: auth.PrivilegeObject_TABLE, - Schema: schemaName, - Name: table.Name, - }, userRole.ID()) - for _, privilege := range g.GrantTable.Privileges { - if !userRole.IsSuperUser && !isOwner && !auth.HasTablePrivilegeGrantOption(key, privilege) { - // TODO: grab the actual error message - err = fmt.Errorf(`role "%s" does not have permission to grant this privilege`, userRole.Name) - return - } - auth.AddTablePrivilege(key, auth.GrantedPrivilege{ - Privilege: privilege, - GrantedBy: grantedByID, - }, g.WithGrantOption) - } - } + case g.GrantRole != nil: + if err = g.grantRole(ctx); err != nil { + return } default: err = fmt.Errorf("GRANT statement is not yet supported") @@ -176,3 +139,155 @@ func (g *Grant) WithResolvedChildren(children []any) (any, error) { } return g, nil } + +// common handles the initial logic for each GRANT statement. `roles` are the `ToRoles`. `userRole` is the role of the +// session's selected user. +func (g *Grant) common(ctx *sql.Context) (roles []auth.Role, userRole auth.Role, err error) { + roles = make([]auth.Role, len(g.ToRoles)) + // First we'll verify that all of the roles exist + for i, roleName := range g.ToRoles { + roles[i] = auth.GetRole(roleName) + if !roles[i].IsValid() { + return nil, auth.Role{}, fmt.Errorf(`role "%s" does not exist`, roleName) + } + } + // Then we'll check that the role that is granting the privileges exists + userRole = auth.GetRole(ctx.Client().User) + if !userRole.IsValid() { + return nil, auth.Role{}, fmt.Errorf(`role "%s" does not exist`, ctx.Client().User) + } + if len(g.GrantedBy) != 0 { + grantedByRole := auth.GetRole(g.GrantedBy) + if !grantedByRole.IsValid() { + return nil, auth.Role{}, fmt.Errorf(`role "%s" does not exist`, g.GrantedBy) + } + if userRole.ID() != grantedByRole.ID() { + // TODO: grab the actual error message + return nil, auth.Role{}, errors.New("GRANTED BY may only be set to the calling user") + } + } + return roles, userRole, nil +} + +// grantTable handles *GrantTable from within RowIter. +func (g *Grant) grantTable(ctx *sql.Context) error { + roles, userRole, err := g.common(ctx) + if err != nil { + return err + } + for _, role := range roles { + for _, table := range g.GrantTable.Tables { + schemaName, err := core.GetSchemaName(ctx, nil, table.Schema) + if err != nil { + return err + } + key := auth.TablePrivilegeKey{ + Role: userRole.ID(), + Table: doltdb.TableName{Name: table.Name, Schema: schemaName}, + } + for _, privilege := range g.GrantTable.Privileges { + grantedBy := auth.HasTablePrivilegeGrantOption(key, privilege) + if !grantedBy.IsValid() { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to grant this privilege`, userRole.Name) + } + auth.AddTablePrivilege(auth.TablePrivilegeKey{ + Role: role.ID(), + Table: doltdb.TableName{Name: table.Name, Schema: schemaName}, + }, auth.GrantedPrivilege{ + Privilege: privilege, + GrantedBy: grantedBy, + }, g.WithGrantOption) + } + } + } + return nil +} + +// grantSchema handles *GrantSchema from within RowIter. +func (g *Grant) grantSchema(ctx *sql.Context) error { + roles, userRole, err := g.common(ctx) + if err != nil { + return err + } + for _, role := range roles { + for _, schema := range g.GrantSchema.Schemas { + key := auth.SchemaPrivilegeKey{ + Role: userRole.ID(), + Schema: schema, + } + for _, privilege := range g.GrantSchema.Privileges { + grantedBy := auth.HasSchemaPrivilegeGrantOption(key, privilege) + if !grantedBy.IsValid() { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to grant this privilege`, userRole.Name) + } + auth.AddSchemaPrivilege(auth.SchemaPrivilegeKey{ + Role: role.ID(), + Schema: schema, + }, auth.GrantedPrivilege{ + Privilege: privilege, + GrantedBy: grantedBy, + }, g.WithGrantOption) + } + } + } + return nil +} + +// grantDatabase handles *GrantDatabase from within RowIter. +func (g *Grant) grantDatabase(ctx *sql.Context) error { + roles, userRole, err := g.common(ctx) + if err != nil { + return err + } + for _, role := range roles { + for _, database := range g.GrantDatabase.Databases { + key := auth.DatabasePrivilegeKey{ + Role: userRole.ID(), + Name: database, + } + for _, privilege := range g.GrantDatabase.Privileges { + grantedBy := auth.HasDatabasePrivilegeGrantOption(key, privilege) + if !grantedBy.IsValid() { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to grant this privilege`, userRole.Name) + } + auth.AddDatabasePrivilege(auth.DatabasePrivilegeKey{ + Role: role.ID(), + Name: database, + }, auth.GrantedPrivilege{ + Privilege: privilege, + GrantedBy: grantedBy, + }, g.WithGrantOption) + } + } + } + return nil +} + +// grantRole handles *GrantRole from within RowIter. +func (g *Grant) grantRole(ctx *sql.Context) error { + members, userRole, err := g.common(ctx) + if err != nil { + return err + } + groups := make([]auth.Role, len(g.GrantRole.Groups)) + for i, groupName := range g.GrantRole.Groups { + groups[i] = auth.GetRole(groupName) + if !groups[i].IsValid() { + return fmt.Errorf(`role "%s" does not exist`, groupName) + } + } + for _, member := range members { + for _, group := range groups { + memberGroupID, _, withAdminOption := auth.IsRoleAMember(userRole.ID(), group.ID()) + if !memberGroupID.IsValid() || !withAdminOption { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to grant role "%s"`, userRole.Name, group.Name) + } + auth.AddMemberToGroup(member.ID(), group.ID(), g.WithGrantOption, memberGroupID) + } + } + return nil +} diff --git a/server/node/revoke.go b/server/node/revoke.go index 17f1a047eb..5e89d4de59 100644 --- a/server/node/revoke.go +++ b/server/node/revoke.go @@ -30,27 +30,41 @@ import ( // Revoke handles all of the REVOKE statements. type Revoke struct { RevokeTable *RevokeTable + RevokeSchema *RevokeSchema + RevokeDatabase *RevokeDatabase + RevokeRole *RevokeRole FromRoles []string GrantedBy string - GrantOptionFor bool + GrantOptionFor bool // This is "ADMIN OPTION FOR" for RevokeRole only Cascade bool // When false, represents RESTRICT } // RevokeTable specifically handles the REVOKE ... ON TABLE statement. type RevokeTable struct { - Privileges []auth.Privilege - Tables []doltdb.TableName - AllTablesInSchemas []string + Privileges []auth.Privilege + Tables []doltdb.TableName } -var _ sql.ExecSourceRel = (*Revoke)(nil) -var _ vitess.Injectable = (*Revoke)(nil) +// RevokeSchema specifically handles the REVOKE ... ON SCHEMA statement. +type RevokeSchema struct { + Privileges []auth.Privilege + Schemas []string +} -// CheckPrivileges implements the interface sql.ExecSourceRel. -func (r *Revoke) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { - return true +// RevokeDatabase specifically handles the REVOKE ... ON DATABASE statement. +type RevokeDatabase struct { + Privileges []auth.Privilege + Databases []string } +// RevokeRole specifically handles the REVOKE FROM statement. +type RevokeRole struct { + Groups []string +} + +var _ sql.ExecSourceRel = (*Revoke)(nil) +var _ vitess.Injectable = (*Revoke)(nil) + // Children implements the interface sql.ExecSourceRel. func (r *Revoke) Children() []sql.Node { return nil @@ -76,64 +90,20 @@ func (r *Revoke) RowIter(ctx *sql.Context, _ sql.Row) (sql.RowIter, error) { auth.LockWrite(func() { switch { case r.RevokeTable != nil: - if len(r.RevokeTable.AllTablesInSchemas) > 0 { - err = fmt.Errorf("revoking privileges to all tables in the schema is not yet supported") + if err = r.revokeTable(ctx); err != nil { return } - roles := make([]auth.Role, len(r.FromRoles)) - // First we'll verify that all of the roles exist - for i, roleName := range r.FromRoles { - roles[i] = auth.GetRole(roleName) - if !roles[i].IsValid() { - err = fmt.Errorf(`role "%s" does not exist`, roleName) - return - } - } - // Then we'll check that the role that is revoking the privileges exists - userRole := auth.GetRole(ctx.Client().User) - if !userRole.IsValid() { - err = fmt.Errorf(`role "%s" does not exist`, ctx.Client().User) + case r.RevokeSchema != nil: + if err = r.revokeSchema(ctx); err != nil { return } - var grantedByID auth.RoleID - if len(r.GrantedBy) != 0 { - grantedByRole := auth.GetRole(r.GrantedBy) - if !grantedByRole.IsValid() { - err = fmt.Errorf(`role "%s" does not exist`, r.GrantedBy) - return - } - grantedByID = grantedByRole.ID() + case r.RevokeDatabase != nil: + if err = r.revokeDatabase(ctx); err != nil { + return } - // Next we'll remove the privileges - for _, role := range roles { - for _, table := range r.RevokeTable.Tables { - var schemaName string - schemaName, err = core.GetSchemaName(ctx, nil, table.Schema) - if err != nil { - return - } - key := auth.TablePrivilegeKey{ - Role: role.ID(), - Table: doltdb.TableName{Name: table.Name, Schema: schemaName}, - } - isOwner := auth.IsOwner(auth.OwnershipKey{ - PrivilegeObject: auth.PrivilegeObject_TABLE, - Schema: schemaName, - Name: table.Name, - }, userRole.ID()) - for _, privilege := range r.RevokeTable.Privileges { - // TODO: we don't have to exactly match the GRANTED BY ID, we can also check if it's in the access chain - if !userRole.IsSuperUser && !isOwner && userRole.ID() != grantedByID { - // TODO: grab the actual error message - err = fmt.Errorf(`role "%s" does not have permission to revoke this privilege`, userRole.Name) - return - } - auth.RemoveTablePrivilege(key, auth.GrantedPrivilege{ - Privilege: privilege, - GrantedBy: grantedByID, - }, r.GrantOptionFor) - } - } + case r.RevokeRole != nil: + if err = r.revokeRole(ctx); err != nil { + return } default: err = fmt.Errorf("REVOKE statement is not yet supported") @@ -174,3 +144,154 @@ func (r *Revoke) WithResolvedChildren(children []any) (any, error) { } return r, nil } + +// common handles the initial logic for each REVOKE statement. `roles` are the `FromRoles`. `userRole` is the role of +// the session's selected user. `grantedByID` is the `GrantedBy` user if specified (or `userRole` if not). +func (r *Revoke) common(ctx *sql.Context) (roles []auth.Role, userRole auth.Role, grantedByID auth.RoleID, err error) { + roles = make([]auth.Role, len(r.FromRoles)) + // First we'll verify that all of the roles exist + for i, roleName := range r.FromRoles { + roles[i] = auth.GetRole(roleName) + if !roles[i].IsValid() { + return nil, auth.Role{}, 0, fmt.Errorf(`role "%s" does not exist`, roleName) + } + } + // Then we'll check that the role that is revoking the privileges exists + userRole = auth.GetRole(ctx.Client().User) + if !userRole.IsValid() { + return nil, auth.Role{}, 0, fmt.Errorf(`role "%s" does not exist`, ctx.Client().User) + } + if len(r.GrantedBy) != 0 { + grantedByRole := auth.GetRole(r.GrantedBy) + if !grantedByRole.IsValid() { + return nil, auth.Role{}, 0, fmt.Errorf(`role "%s" does not exist`, r.GrantedBy) + } + if groupID, _, _ := auth.IsRoleAMember(userRole.ID(), grantedByRole.ID()); !groupID.IsValid() { + // TODO: grab the actual error message + return nil, auth.Role{}, 0, fmt.Errorf(`role "%s" does not have permission to revoke this privilege`, userRole.Name) + } + } else { + grantedByID = userRole.ID() + } + return roles, userRole, grantedByID, nil +} + +// revokeTable handles *RevokeTable from within RowIter. +func (r *Revoke) revokeTable(ctx *sql.Context) error { + roles, userRole, grantedByID, err := r.common(ctx) + if err != nil { + return err + } + for _, role := range roles { + for _, table := range r.RevokeTable.Tables { + schemaName, err := core.GetSchemaName(ctx, nil, table.Schema) + if err != nil { + return err + } + key := auth.TablePrivilegeKey{ + Role: userRole.ID(), + Table: doltdb.TableName{Name: table.Name, Schema: schemaName}, + } + for _, privilege := range r.RevokeTable.Privileges { + if id := auth.HasTablePrivilegeGrantOption(key, privilege); !id.IsValid() { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to revoke this privilege`, userRole.Name) + } + auth.RemoveTablePrivilege(auth.TablePrivilegeKey{ + Role: role.ID(), + Table: doltdb.TableName{Name: table.Name, Schema: schemaName}, + }, auth.GrantedPrivilege{ + Privilege: privilege, + GrantedBy: grantedByID, + }, r.GrantOptionFor) + } + } + } + return nil +} + +// revokeSchema handles *RevokeSchema from within RowIter. +func (r *Revoke) revokeSchema(ctx *sql.Context) error { + roles, userRole, grantedByID, err := r.common(ctx) + if err != nil { + return err + } + for _, role := range roles { + for _, schema := range r.RevokeSchema.Schemas { + key := auth.SchemaPrivilegeKey{ + Role: userRole.ID(), + Schema: schema, + } + for _, privilege := range r.RevokeTable.Privileges { + if id := auth.HasSchemaPrivilegeGrantOption(key, privilege); !id.IsValid() { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to revoke this privilege`, userRole.Name) + } + auth.RemoveSchemaPrivilege(auth.SchemaPrivilegeKey{ + Role: role.ID(), + Schema: schema, + }, auth.GrantedPrivilege{ + Privilege: privilege, + GrantedBy: grantedByID, + }, r.GrantOptionFor) + } + } + } + return nil +} + +// revokeDatabase handles *RevokeDatabase from within RowIter. +func (r *Revoke) revokeDatabase(ctx *sql.Context) error { + roles, userRole, grantedByID, err := r.common(ctx) + if err != nil { + return err + } + for _, role := range roles { + for _, databases := range r.RevokeDatabase.Databases { + key := auth.DatabasePrivilegeKey{ + Role: userRole.ID(), + Name: databases, + } + for _, privilege := range r.RevokeDatabase.Privileges { + if id := auth.HasDatabasePrivilegeGrantOption(key, privilege); !id.IsValid() { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to revoke this privilege`, userRole.Name) + } + auth.RemoveDatabasePrivilege(auth.DatabasePrivilegeKey{ + Role: role.ID(), + Name: databases, + }, auth.GrantedPrivilege{ + Privilege: privilege, + GrantedBy: grantedByID, + }, r.GrantOptionFor) + } + } + } + return nil +} + +// revokeRole handles *RevokeRole from within RowIter. +func (r *Revoke) revokeRole(ctx *sql.Context) error { + members, userRole, _, err := r.common(ctx) + if err != nil { + return err + } + groups := make([]auth.Role, len(r.RevokeRole.Groups)) + for i, groupName := range r.RevokeRole.Groups { + groups[i] = auth.GetRole(groupName) + if !groups[i].IsValid() { + return fmt.Errorf(`role "%s" does not exist`, groupName) + } + } + for _, member := range members { + for _, group := range groups { + memberGroupID, _, withAdminOption := auth.IsRoleAMember(userRole.ID(), group.ID()) + if !memberGroupID.IsValid() || !withAdminOption { + // TODO: grab the actual error message + return fmt.Errorf(`role "%s" does not have permission to revoke role "%s"`, userRole.Name, group.Name) + } + auth.RemoveMemberFromGroup(member.ID(), group.ID(), r.GrantOptionFor) + } + } + return nil +} diff --git a/server/tables/dtables/ignore.go b/server/tables/dtables/ignore.go new file mode 100644 index 0000000000..7c829c8495 --- /dev/null +++ b/server/tables/dtables/ignore.go @@ -0,0 +1,57 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dtables + +import ( + "fmt" + + "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" + "github.com/dolthub/dolt/go/store/val" + "github.com/dolthub/go-mysql-server/sql" + + pgtypes "github.com/dolthub/doltgresql/server/types" +) + +// getDoltIgnoreSchema returns the schema for the dolt_ignore table. +func getDoltIgnoreSchema() sql.Schema { + return []*sql.Column{ + {Name: "pattern", Type: pgtypes.Text, Source: doltdb.IgnoreTableName, PrimaryKey: true}, + {Name: "ignored", Type: pgtypes.Bool, Source: doltdb.IgnoreTableName, PrimaryKey: false, Nullable: false}, + } +} + +// convertTupleToIgnoreBoolean reads a boolean from a tuple and returns it. +func convertTupleToIgnoreBoolean(valueDesc val.TupleDesc, valueTuple val.Tuple) (bool, error) { + extendedTuple := val.NewTupleDescriptorWithArgs( + val.TupleDescriptorArgs{Comparator: valueDesc.Comparator(), Handlers: valueDesc.Handlers}, + val.Type{Enc: val.ExtendedEnc, Nullable: false}, + ) + if !valueDesc.Equals(extendedTuple) { + return false, fmt.Errorf("dolt_ignore had unexpected value type, this should never happen") + } + extended, ok := valueDesc.GetExtended(0, valueTuple) + if !ok { + return false, fmt.Errorf("could not read boolean") + } + val, err := valueDesc.Handlers[0].DeserializeValue(extended) + if err != nil { + return false, err + } + ignore, ok := val.(bool) + if !ok { + return false, fmt.Errorf("could not read boolean") + } + return ignore, nil +} diff --git a/server/tables/dtables/init.go b/server/tables/dtables/init.go index ef786ce73c..2728a55518 100644 --- a/server/tables/dtables/init.go +++ b/server/tables/dtables/init.go @@ -16,6 +16,8 @@ package dtables import ( "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dprocedures" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" ) @@ -30,6 +32,7 @@ func Init() { doltdb.GetDiffTableName = getDiffTableName doltdb.GetLogTableName = getLogTableName doltdb.GetMergeStatusTableName = getMergeStatusTableName + doltdb.GetRebaseTableName = getRebaseTableName doltdb.GetRemoteBranchesTableName = getRemoteBranchesTableName doltdb.GetRemotesTableName = getRemotesTableName doltdb.GetSchemaConflictsTableName = getSchemaConflictsTableName @@ -40,6 +43,13 @@ func Init() { // Schemas dtables.GetDocsSchema = getDocsSchema + dtables.GetDoltIgnoreSchema = getDoltIgnoreSchema + dprocedures.GetDoltRebaseSystemTableSchema = getRebaseSchema + + // Conversions + doltdb.ConvertTupleToIgnoreBoolean = convertTupleToIgnoreBoolean + sqle.ConvertRebasePlanStepToRow = convertRebasePlanStepToRow + sqle.ConvertRowToRebasePlanStep = convertRowToRebasePlanStep } // getBranchesTableName returns the name of the branches table. diff --git a/server/tables/dtables/rebase.go b/server/tables/dtables/rebase.go new file mode 100644 index 0000000000..6027942d3c --- /dev/null +++ b/server/tables/dtables/rebase.go @@ -0,0 +1,82 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package dtables + +import ( + "fmt" + "strings" + + "github.com/dolthub/dolt/go/libraries/doltcore/rebase" + "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dprocedures" + "github.com/dolthub/go-mysql-server/sql" + "github.com/shopspring/decimal" + + pgtypes "github.com/dolthub/doltgresql/server/types" +) + +// getRebaseSchema returns the schema for the rebase table. +func getRebaseSchema() sql.Schema { + return []*sql.Column{ + {Name: "rebase_order", Type: pgtypes.Float32, Nullable: false, PrimaryKey: true}, // TODO: cannot have numeric key + {Name: "action", Type: pgtypes.VarCharType{MaxChars: 6}, Nullable: false}, // TODO: Should be enum(pick, squash, fixup, drop, reword) + {Name: "commit_hash", Type: pgtypes.Text, Nullable: false}, + {Name: "commit_message", Type: pgtypes.Text, Nullable: false}, + } +} + +// convertRebasePlanStepToRow converts a RebasePlanStep to a sql.Row. +func convertRebasePlanStepToRow(planMember rebase.RebasePlanStep) (sql.Row, error) { + actionEnumValue := dprocedures.RebaseActionEnumType.IndexOf(strings.ToLower(planMember.Action)) + if actionEnumValue == -1 { + return nil, fmt.Errorf("invalid rebase action: %s", planMember.Action) + } + + return sql.Row{ + planMember.RebaseOrderAsFloat(), + planMember.Action, + planMember.CommitHash, + planMember.CommitMsg, + }, nil +} + +// convertRowToRebasePlanStep converts a sql.Row to a RebasePlanStep. +func convertRowToRebasePlanStep(row sql.Row) (rebase.RebasePlanStep, error) { + order, ok := row[0].(float32) + if !ok { + return rebase.RebasePlanStep{}, fmt.Errorf("invalid order value in rebase plan: %v (%T)", row[0], row[0]) + } + + rebaseAction, ok := row[1].(string) + if !ok { + return rebase.RebasePlanStep{}, fmt.Errorf("invalid enum value in rebase plan: %v (%T)", row[1], row[1]) + } + + rebaseIdx := dprocedures.RebaseActionEnumType.IndexOf(rebaseAction) + if rebaseIdx < 0 { + return rebase.RebasePlanStep{}, fmt.Errorf("invalid enum value in rebase plan: %v (%T)", row[1], row[1]) + } + + return rebase.RebasePlanStep{ + RebaseOrder: decimal.NewFromFloat32(order), + Action: rebaseAction, + CommitHash: row[2].(string), + CommitMsg: row[3].(string), + }, nil +} + +// getRebaseTableName returns the name of the rebase table. +func getRebaseTableName() string { + return "rebase" +} diff --git a/testing/generation/command_docs/output/grant_test.go b/testing/generation/command_docs/output/grant_test.go index dbc57aded4..52a2029167 100644 --- a/testing/generation/command_docs/output/grant_test.go +++ b/testing/generation/command_docs/output/grant_test.go @@ -2554,169 +2554,169 @@ func TestGrant(t *testing.T) { Parses("GRANT SELECT ON SEQUENCE sequence_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), Parses("GRANT USAGE ON ALL SEQUENCES IN SCHEMA schema_name , schema_name TO CURRENT_USER , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), Parses("GRANT USAGE , UPDATE ON SEQUENCE sequence_name , sequence_name TO SESSION_USER , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_USER"), - Parses("GRANT CREATE ON DATABASE database_name TO role_name , role_name"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name , database_name TO role_name , role_name"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO role_name , role_name"), - Parses("GRANT TEMP , TEMP ON DATABASE database_name , database_name TO role_name , role_name"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name TO CURRENT_ROLE , role_name"), - Parses("GRANT CONNECT , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_ROLE , role_name"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO SESSION_USER , role_name"), - Parses("GRANT CONNECT , TEMPORARY ON DATABASE database_name TO CURRENT_USER , PUBLIC"), - Parses("GRANT ALL ON DATABASE database_name TO SESSION_USER , PUBLIC"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC"), - Parses("GRANT ALL ON DATABASE database_name TO role_name , CURRENT_ROLE"), - Parses("GRANT TEMPORARY , CREATE ON DATABASE database_name TO role_name , SESSION_USER"), - Parses("GRANT TEMP ON DATABASE database_name , database_name TO role_name , SESSION_USER"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_USER , SESSION_USER"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name TO PUBLIC WITH GRANT OPTION"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO PUBLIC WITH GRANT OPTION"), - Parses("GRANT CREATE , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE WITH GRANT OPTION"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE WITH GRANT OPTION"), - Parses("GRANT CREATE , TEMP ON DATABASE database_name TO PUBLIC , role_name WITH GRANT OPTION"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_USER , role_name WITH GRANT OPTION"), - Parses("GRANT TEMPORARY ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION"), - Parses("GRANT TEMPORARY , CONNECT ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION"), - Parses("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION"), - Parses("GRANT CONNECT ON DATABASE database_name TO CURRENT_USER , CURRENT_USER WITH GRANT OPTION"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name , database_name TO role_name GRANTED BY role_name"), - Parses("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC GRANTED BY role_name"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO PUBLIC GRANTED BY role_name"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO role_name , role_name GRANTED BY role_name"), - Parses("GRANT CREATE ON DATABASE database_name , database_name TO CURRENT_USER , role_name GRANTED BY role_name"), - Parses("GRANT TEMP , TEMP ON DATABASE database_name TO SESSION_USER , role_name GRANTED BY role_name"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO SESSION_USER , PUBLIC GRANTED BY role_name"), - Parses("GRANT TEMPORARY , CREATE ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC GRANTED BY role_name"), - Parses("GRANT TEMP ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), - Parses("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE GRANTED BY role_name"), - Parses("GRANT CREATE , TEMP ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE GRANTED BY role_name"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE , CURRENT_ROLE GRANTED BY role_name"), - Parses("GRANT TEMPORARY , CREATE ON DATABASE database_name , database_name TO role_name , CURRENT_USER GRANTED BY role_name"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name TO PUBLIC , CURRENT_USER GRANTED BY role_name"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY role_name"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name TO CURRENT_USER , CURRENT_USER GRANTED BY role_name"), - Parses("GRANT TEMPORARY ON DATABASE database_name , database_name TO role_name , SESSION_USER GRANTED BY role_name"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO PUBLIC , SESSION_USER GRANTED BY role_name"), - Parses("GRANT TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER GRANTED BY role_name"), - Parses("GRANT CREATE , TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER GRANTED BY role_name"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name TO SESSION_USER , SESSION_USER GRANTED BY role_name"), - Parses("GRANT CREATE , TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY role_name"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name , database_name TO PUBLIC WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name TO CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT TEMP , TEMP ON DATABASE database_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CREATE , TEMP ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT TEMP ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CREATE ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO PUBLIC , SESSION_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name TO PUBLIC GRANTED BY PUBLIC"), - Parses("GRANT TEMP , CREATE ON DATABASE database_name TO PUBLIC , role_name GRANTED BY PUBLIC"), - Parses("GRANT TEMP , TEMP ON DATABASE database_name , database_name TO PUBLIC , role_name GRANTED BY PUBLIC"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC GRANTED BY PUBLIC"), - Parses("GRANT CREATE ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO PUBLIC , CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name , database_name TO CURRENT_USER , CURRENT_USER GRANTED BY PUBLIC"), - Parses("GRANT CONNECT ON DATABASE database_name TO SESSION_USER , CURRENT_USER GRANTED BY PUBLIC"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER GRANTED BY PUBLIC"), - Parses("GRANT CREATE , TEMP ON DATABASE database_name , database_name TO CURRENT_USER , SESSION_USER GRANTED BY PUBLIC"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name TO SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CONNECT , TEMPORARY ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CONNECT ON DATABASE database_name , database_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO role_name , SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CREATE , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO role_name , role_name GRANTED BY CURRENT_ROLE"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO PUBLIC , role_name GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMP , TEMP ON DATABASE database_name TO CURRENT_USER , role_name GRANTED BY CURRENT_ROLE"), - Parses("GRANT CONNECT , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC , PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO PUBLIC , PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY , CONNECT ON DATABASE database_name TO SESSION_USER , PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY ON DATABASE database_name TO role_name , CURRENT_USER GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMP , CREATE ON DATABASE database_name TO role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT CREATE , TEMP ON DATABASE database_name TO role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name TO CURRENT_ROLE , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT CREATE , TEMPORARY ON DATABASE database_name TO CURRENT_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO CURRENT_ROLE , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMP ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT CREATE , TEMPORARY ON DATABASE database_name TO role_name GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_USER GRANTED BY CURRENT_USER"), - Parses("GRANT TEMP , CREATE ON DATABASE database_name , database_name TO SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT CONNECT ON DATABASE database_name , database_name TO CURRENT_USER , role_name GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name TO SESSION_USER , role_name GRANTED BY CURRENT_USER"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO PUBLIC , PUBLIC GRANTED BY CURRENT_USER"), - Parses("GRANT TEMPORARY ON DATABASE database_name TO PUBLIC , CURRENT_ROLE GRANTED BY CURRENT_USER"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_ROLE GRANTED BY CURRENT_USER"), - Parses("GRANT TEMP ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_USER"), - Parses("GRANT ALL ON DATABASE database_name , database_name TO role_name , SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name TO PUBLIC , SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO CURRENT_ROLE , SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO role_name , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT ALL ON DATABASE database_name , database_name TO CURRENT_ROLE , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO CURRENT_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO role_name , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT TEMP , TEMPORARY ON DATABASE database_name TO SESSION_USER , SESSION_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO role_name GRANTED BY SESSION_USER"), - Parses("GRANT ALL PRIVILEGES ON DATABASE database_name TO CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT CREATE , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE , role_name GRANTED BY SESSION_USER"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , PUBLIC GRANTED BY SESSION_USER"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name TO CURRENT_ROLE , PUBLIC GRANTED BY SESSION_USER"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , PUBLIC GRANTED BY SESSION_USER"), - Parses("GRANT CREATE ON DATABASE database_name TO PUBLIC , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT TEMPORARY ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name TO PUBLIC WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , PUBLIC WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT TEMPORARY , CREATE ON DATABASE database_name , database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT CREATE , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT CONNECT , TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT TEMP , CONNECT ON DATABASE database_name TO PUBLIC , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT CONNECT , CREATE ON DATABASE database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT CREATE , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_USER"), + Converts("GRANT CREATE ON DATABASE database_name TO role_name , role_name"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name , database_name TO role_name , role_name"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO role_name , role_name"), + Converts("GRANT TEMP , TEMP ON DATABASE database_name , database_name TO role_name , role_name"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name TO CURRENT_ROLE , role_name"), + Converts("GRANT CONNECT , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_ROLE , role_name"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO SESSION_USER , role_name"), + Converts("GRANT CONNECT , TEMPORARY ON DATABASE database_name TO CURRENT_USER , PUBLIC"), + Converts("GRANT ALL ON DATABASE database_name TO SESSION_USER , PUBLIC"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC"), + Converts("GRANT ALL ON DATABASE database_name TO role_name , CURRENT_ROLE"), + Converts("GRANT TEMPORARY , CREATE ON DATABASE database_name TO role_name , SESSION_USER"), + Converts("GRANT TEMP ON DATABASE database_name , database_name TO role_name , SESSION_USER"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_USER , SESSION_USER"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name TO PUBLIC WITH GRANT OPTION"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO PUBLIC WITH GRANT OPTION"), + Converts("GRANT CREATE , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE WITH GRANT OPTION"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE WITH GRANT OPTION"), + Converts("GRANT CREATE , TEMP ON DATABASE database_name TO PUBLIC , role_name WITH GRANT OPTION"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_USER , role_name WITH GRANT OPTION"), + Converts("GRANT TEMPORARY ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION"), + Converts("GRANT TEMPORARY , CONNECT ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION"), + Converts("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION"), + Converts("GRANT CONNECT ON DATABASE database_name TO CURRENT_USER , CURRENT_USER WITH GRANT OPTION"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name , database_name TO role_name GRANTED BY role_name"), + Converts("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC GRANTED BY role_name"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO PUBLIC GRANTED BY role_name"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO role_name , role_name GRANTED BY role_name"), + Converts("GRANT CREATE ON DATABASE database_name , database_name TO CURRENT_USER , role_name GRANTED BY role_name"), + Converts("GRANT TEMP , TEMP ON DATABASE database_name TO SESSION_USER , role_name GRANTED BY role_name"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO SESSION_USER , PUBLIC GRANTED BY role_name"), + Converts("GRANT TEMPORARY , CREATE ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC GRANTED BY role_name"), + Converts("GRANT TEMP ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY role_name"), + Converts("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE GRANTED BY role_name"), + Converts("GRANT CREATE , TEMP ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE GRANTED BY role_name"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE , CURRENT_ROLE GRANTED BY role_name"), + Converts("GRANT TEMPORARY , CREATE ON DATABASE database_name , database_name TO role_name , CURRENT_USER GRANTED BY role_name"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name TO PUBLIC , CURRENT_USER GRANTED BY role_name"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY role_name"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name TO CURRENT_USER , CURRENT_USER GRANTED BY role_name"), + Converts("GRANT TEMPORARY ON DATABASE database_name , database_name TO role_name , SESSION_USER GRANTED BY role_name"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO PUBLIC , SESSION_USER GRANTED BY role_name"), + Converts("GRANT TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER GRANTED BY role_name"), + Converts("GRANT CREATE , TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER GRANTED BY role_name"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name TO SESSION_USER , SESSION_USER GRANTED BY role_name"), + Converts("GRANT CREATE , TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY role_name"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name , database_name TO PUBLIC WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name TO CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT TEMP , TEMP ON DATABASE database_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CREATE , TEMP ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT TEMP ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CREATE ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO PUBLIC , SESSION_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name TO PUBLIC GRANTED BY PUBLIC"), + Converts("GRANT TEMP , CREATE ON DATABASE database_name TO PUBLIC , role_name GRANTED BY PUBLIC"), + Converts("GRANT TEMP , TEMP ON DATABASE database_name , database_name TO PUBLIC , role_name GRANTED BY PUBLIC"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO SESSION_USER , PUBLIC GRANTED BY PUBLIC"), + Converts("GRANT CREATE ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO PUBLIC , CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name , database_name TO CURRENT_USER , CURRENT_USER GRANTED BY PUBLIC"), + Converts("GRANT CONNECT ON DATABASE database_name TO SESSION_USER , CURRENT_USER GRANTED BY PUBLIC"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER GRANTED BY PUBLIC"), + Converts("GRANT CREATE , TEMP ON DATABASE database_name , database_name TO CURRENT_USER , SESSION_USER GRANTED BY PUBLIC"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name TO SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CONNECT , TEMPORARY ON DATABASE database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CONNECT ON DATABASE database_name , database_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO role_name , SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_USER , SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CREATE , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO role_name , role_name GRANTED BY CURRENT_ROLE"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO PUBLIC , role_name GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMP , TEMP ON DATABASE database_name TO CURRENT_USER , role_name GRANTED BY CURRENT_ROLE"), + Converts("GRANT CONNECT , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC , PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("GRANT CONNECT , TEMP ON DATABASE database_name , database_name TO PUBLIC , PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY , CONNECT ON DATABASE database_name TO SESSION_USER , PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY ON DATABASE database_name TO role_name , CURRENT_USER GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMP , CREATE ON DATABASE database_name TO role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT CREATE , TEMP ON DATABASE database_name TO role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY , CONNECT ON DATABASE database_name , database_name TO PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name TO CURRENT_ROLE , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT CREATE , TEMPORARY ON DATABASE database_name TO CURRENT_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO CURRENT_ROLE , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMP ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT TEMPORARY , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT CREATE , TEMPORARY ON DATABASE database_name TO role_name GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_USER GRANTED BY CURRENT_USER"), + Converts("GRANT TEMP , CREATE ON DATABASE database_name , database_name TO SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT CONNECT ON DATABASE database_name , database_name TO CURRENT_USER , role_name GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name TO SESSION_USER , role_name GRANTED BY CURRENT_USER"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO PUBLIC , PUBLIC GRANTED BY CURRENT_USER"), + Converts("GRANT TEMPORARY ON DATABASE database_name TO PUBLIC , CURRENT_ROLE GRANTED BY CURRENT_USER"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_ROLE GRANTED BY CURRENT_USER"), + Converts("GRANT TEMP ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , CREATE ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_USER"), + Converts("GRANT ALL ON DATABASE database_name , database_name TO role_name , SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name TO PUBLIC , SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO CURRENT_ROLE , SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO role_name , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT ALL ON DATABASE database_name , database_name TO CURRENT_ROLE , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO CURRENT_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO role_name , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE ON DATABASE database_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT TEMP , TEMPORARY ON DATABASE database_name TO SESSION_USER , SESSION_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name , database_name TO role_name GRANTED BY SESSION_USER"), + Converts("GRANT ALL PRIVILEGES ON DATABASE database_name TO CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT CREATE , TEMP ON DATABASE database_name , database_name TO CURRENT_ROLE , role_name GRANTED BY SESSION_USER"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , PUBLIC GRANTED BY SESSION_USER"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name TO CURRENT_ROLE , PUBLIC GRANTED BY SESSION_USER"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , PUBLIC GRANTED BY SESSION_USER"), + Converts("GRANT CREATE ON DATABASE database_name TO PUBLIC , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO CURRENT_ROLE , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT TEMPORARY ON DATABASE database_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name TO PUBLIC WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT CONNECT , CONNECT ON DATABASE database_name TO role_name , PUBLIC WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT TEMPORARY , CREATE ON DATABASE database_name , database_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT CREATE , CONNECT ON DATABASE database_name , database_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT TEMPORARY , TEMPORARY ON DATABASE database_name , database_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT CONNECT , TEMPORARY ON DATABASE database_name , database_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT TEMP , CONNECT ON DATABASE database_name TO PUBLIC , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT CONNECT , CREATE ON DATABASE database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT CREATE , TEMPORARY ON DATABASE database_name , database_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), Parses("GRANT USAGE ON DOMAIN domain_name , domain_name TO CURRENT_ROLE , PUBLIC"), Parses("GRANT ALL PRIVILEGES ON DOMAIN domain_name TO CURRENT_USER , PUBLIC"), Parses("GRANT USAGE ON DOMAIN domain_name , domain_name TO CURRENT_USER , CURRENT_ROLE"), @@ -9909,55 +9909,55 @@ func TestGrant(t *testing.T) { Parses("GRANT SET , ALTER SYSTEM ON PARAMETER configuration_parameter TO PUBLIC WITH GRANT OPTION GRANTED BY SESSION_USER"), Parses("GRANT SET , SET ON PARAMETER configuration_parameter TO CURRENT_ROLE , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), Parses("GRANT ALL ON PARAMETER configuration_parameter TO CURRENT_USER , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT USAGE ON SCHEMA schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION"), - Parses("GRANT USAGE , USAGE ON SCHEMA schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION"), - Parses("GRANT USAGE ON SCHEMA schema_name TO role_name , SESSION_USER WITH GRANT OPTION"), - Parses("GRANT USAGE ON SCHEMA schema_name TO SESSION_USER , SESSION_USER WITH GRANT OPTION"), - Parses("GRANT CREATE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , PUBLIC GRANTED BY role_name"), - Parses("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , PUBLIC GRANTED BY role_name"), - Parses("GRANT USAGE ON SCHEMA schema_name TO CURRENT_USER , SESSION_USER GRANTED BY role_name"), - Parses("GRANT USAGE ON SCHEMA schema_name TO CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CREATE ON SCHEMA schema_name , schema_name TO CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT USAGE , CREATE ON SCHEMA schema_name TO CURRENT_USER , role_name WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT ALL ON SCHEMA schema_name TO PUBLIC , PUBLIC WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT CREATE ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), - Parses("GRANT USAGE , CREATE ON SCHEMA schema_name TO CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("GRANT ALL ON SCHEMA schema_name , schema_name TO CURRENT_USER GRANTED BY PUBLIC"), - Parses("GRANT CREATE ON SCHEMA schema_name , schema_name TO role_name , role_name WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT USAGE ON SCHEMA schema_name , schema_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT ALL ON SCHEMA schema_name , schema_name TO role_name , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT USAGE ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CREATE , CREATE ON SCHEMA schema_name TO SESSION_USER , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT CREATE ON SCHEMA schema_name TO CURRENT_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT USAGE ON SCHEMA schema_name , schema_name TO role_name , SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), - Parses("GRANT ALL ON SCHEMA schema_name , schema_name TO PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("GRANT CREATE ON SCHEMA schema_name , schema_name TO SESSION_USER GRANTED BY CURRENT_ROLE"), - Parses("GRANT ALL ON SCHEMA schema_name TO CURRENT_USER , role_name GRANTED BY CURRENT_ROLE"), - Parses("GRANT USAGE , CREATE ON SCHEMA schema_name , schema_name TO SESSION_USER , role_name GRANTED BY CURRENT_ROLE"), - Parses("GRANT CREATE ON SCHEMA schema_name TO role_name , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT USAGE ON SCHEMA schema_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT ALL PRIVILEGES ON SCHEMA schema_name , schema_name TO role_name , role_name GRANTED BY CURRENT_USER"), - Parses("GRANT ALL PRIVILEGES ON SCHEMA schema_name , schema_name TO CURRENT_USER , role_name GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_USER"), - Parses("GRANT ALL PRIVILEGES ON SCHEMA schema_name , schema_name TO role_name , CURRENT_ROLE GRANTED BY CURRENT_USER"), - Parses("GRANT ALL ON SCHEMA schema_name , schema_name TO PUBLIC , SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE ON SCHEMA schema_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT USAGE ON SCHEMA schema_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , USAGE ON SCHEMA schema_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT CREATE , USAGE ON SCHEMA schema_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT USAGE , CREATE ON SCHEMA schema_name TO PUBLIC , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT ALL ON SCHEMA schema_name TO SESSION_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , CURRENT_USER GRANTED BY SESSION_USER"), - Parses("GRANT ALL ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER"), - Parses("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO SESSION_USER , SESSION_USER GRANTED BY SESSION_USER"), - Parses("GRANT USAGE ON SCHEMA schema_name TO CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT ALL ON SCHEMA schema_name , schema_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT CREATE , USAGE ON SCHEMA schema_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT USAGE ON SCHEMA schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION"), + Converts("GRANT USAGE , USAGE ON SCHEMA schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION"), + Converts("GRANT USAGE ON SCHEMA schema_name TO role_name , SESSION_USER WITH GRANT OPTION"), + Converts("GRANT USAGE ON SCHEMA schema_name TO SESSION_USER , SESSION_USER WITH GRANT OPTION"), + Converts("GRANT CREATE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , PUBLIC GRANTED BY role_name"), + Converts("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , PUBLIC GRANTED BY role_name"), + Converts("GRANT USAGE ON SCHEMA schema_name TO CURRENT_USER , SESSION_USER GRANTED BY role_name"), + Converts("GRANT USAGE ON SCHEMA schema_name TO CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CREATE ON SCHEMA schema_name , schema_name TO CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT USAGE , CREATE ON SCHEMA schema_name TO CURRENT_USER , role_name WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT ALL ON SCHEMA schema_name TO PUBLIC , PUBLIC WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT CREATE ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , CURRENT_USER WITH GRANT OPTION GRANTED BY role_name"), + Converts("GRANT USAGE , CREATE ON SCHEMA schema_name TO CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("GRANT ALL ON SCHEMA schema_name , schema_name TO CURRENT_USER GRANTED BY PUBLIC"), + Converts("GRANT CREATE ON SCHEMA schema_name , schema_name TO role_name , role_name WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT USAGE ON SCHEMA schema_name , schema_name TO SESSION_USER , role_name WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT ALL ON SCHEMA schema_name , schema_name TO role_name , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT USAGE ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CREATE , CREATE ON SCHEMA schema_name TO SESSION_USER , PUBLIC WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT CREATE ON SCHEMA schema_name TO CURRENT_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT USAGE ON SCHEMA schema_name , schema_name TO role_name , SESSION_USER WITH GRANT OPTION GRANTED BY PUBLIC"), + Converts("GRANT ALL ON SCHEMA schema_name , schema_name TO PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("GRANT CREATE ON SCHEMA schema_name , schema_name TO SESSION_USER GRANTED BY CURRENT_ROLE"), + Converts("GRANT ALL ON SCHEMA schema_name TO CURRENT_USER , role_name GRANTED BY CURRENT_ROLE"), + Converts("GRANT USAGE , CREATE ON SCHEMA schema_name , schema_name TO SESSION_USER , role_name GRANTED BY CURRENT_ROLE"), + Converts("GRANT CREATE ON SCHEMA schema_name TO role_name , role_name WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT USAGE ON SCHEMA schema_name TO PUBLIC , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT ALL PRIVILEGES ON SCHEMA schema_name , schema_name TO role_name , role_name GRANTED BY CURRENT_USER"), + Converts("GRANT ALL PRIVILEGES ON SCHEMA schema_name , schema_name TO CURRENT_USER , role_name GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_USER"), + Converts("GRANT ALL PRIVILEGES ON SCHEMA schema_name , schema_name TO role_name , CURRENT_ROLE GRANTED BY CURRENT_USER"), + Converts("GRANT ALL ON SCHEMA schema_name , schema_name TO PUBLIC , SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , PUBLIC WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE ON SCHEMA schema_name TO CURRENT_USER , CURRENT_ROLE WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT USAGE ON SCHEMA schema_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , USAGE ON SCHEMA schema_name TO SESSION_USER , CURRENT_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT CREATE , USAGE ON SCHEMA schema_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT USAGE , CREATE ON SCHEMA schema_name TO PUBLIC , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT ALL ON SCHEMA schema_name TO SESSION_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT USAGE , USAGE ON SCHEMA schema_name , schema_name TO CURRENT_USER , CURRENT_USER GRANTED BY SESSION_USER"), + Converts("GRANT ALL ON SCHEMA schema_name , schema_name TO CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER"), + Converts("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO SESSION_USER , SESSION_USER GRANTED BY SESSION_USER"), + Converts("GRANT USAGE ON SCHEMA schema_name TO CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT ALL ON SCHEMA schema_name , schema_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT ALL PRIVILEGES ON SCHEMA schema_name TO PUBLIC , CURRENT_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), + Converts("GRANT CREATE , USAGE ON SCHEMA schema_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), Parses("GRANT ALL ON TABLESPACE tablespace_name TO role_name , CURRENT_USER WITH GRANT OPTION"), Parses("GRANT CREATE ON TABLESPACE tablespace_name , tablespace_name TO PUBLIC , PUBLIC WITH GRANT OPTION GRANTED BY role_name"), Parses("GRANT ALL ON TABLESPACE tablespace_name , tablespace_name TO PUBLIC GRANTED BY PUBLIC"), @@ -10007,17 +10007,17 @@ func TestGrant(t *testing.T) { Parses("GRANT ALL ON TYPE type_name TO CURRENT_ROLE , SESSION_USER WITH GRANT OPTION GRANTED BY CURRENT_USER"), Parses("GRANT USAGE ON TYPE type_name TO role_name , CURRENT_ROLE WITH GRANT OPTION GRANTED BY SESSION_USER"), Parses("GRANT ALL PRIVILEGES ON TYPE type_name , type_name TO PUBLIC , SESSION_USER WITH GRANT OPTION GRANTED BY SESSION_USER"), - Parses("GRANT role_name , role_name TO role_name , CURRENT_ROLE"), - Parses("GRANT role_name , role_name TO CURRENT_ROLE , SESSION_USER WITH ADMIN OPTION"), - Parses("GRANT role_name TO PUBLIC , CURRENT_ROLE WITH ADMIN OPTION GRANTED BY role_name"), - Parses("GRANT role_name , role_name TO SESSION_USER , PUBLIC WITH ADMIN OPTION GRANTED BY PUBLIC"), - Parses("GRANT role_name , role_name TO PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("GRANT role_name , role_name TO SESSION_USER WITH ADMIN OPTION GRANTED BY CURRENT_ROLE"), - Parses("GRANT role_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER"), - Parses("GRANT role_name , role_name TO CURRENT_USER , role_name WITH ADMIN OPTION GRANTED BY CURRENT_USER"), - Parses("GRANT role_name TO role_name GRANTED BY SESSION_USER"), - Parses("GRANT role_name , role_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("GRANT role_name TO CURRENT_ROLE , CURRENT_ROLE WITH ADMIN OPTION GRANTED BY SESSION_USER"), + Converts("GRANT role_name , role_name TO role_name , CURRENT_ROLE"), + Converts("GRANT role_name , role_name TO CURRENT_ROLE , SESSION_USER WITH ADMIN OPTION"), + Converts("GRANT role_name TO PUBLIC , CURRENT_ROLE WITH ADMIN OPTION GRANTED BY role_name"), + Converts("GRANT role_name , role_name TO SESSION_USER , PUBLIC WITH ADMIN OPTION GRANTED BY PUBLIC"), + Converts("GRANT role_name , role_name TO PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("GRANT role_name , role_name TO SESSION_USER WITH ADMIN OPTION GRANTED BY CURRENT_ROLE"), + Converts("GRANT role_name TO SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER"), + Converts("GRANT role_name , role_name TO CURRENT_USER , role_name WITH ADMIN OPTION GRANTED BY CURRENT_USER"), + Converts("GRANT role_name TO role_name GRANTED BY SESSION_USER"), + Converts("GRANT role_name , role_name TO CURRENT_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("GRANT role_name TO CURRENT_ROLE , CURRENT_ROLE WITH ADMIN OPTION GRANTED BY SESSION_USER"), } RunTests(t, tests) } diff --git a/testing/generation/command_docs/output/revoke_test.go b/testing/generation/command_docs/output/revoke_test.go index 617cf5c6c5..0347c0ed1e 100644 --- a/testing/generation/command_docs/output/revoke_test.go +++ b/testing/generation/command_docs/output/revoke_test.go @@ -2615,169 +2615,169 @@ func TestRevoke(t *testing.T) { Parses("REVOKE ALL ON ALL SEQUENCES IN SCHEMA schema_name , schema_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), Parses("REVOKE USAGE , UPDATE ON ALL SEQUENCES IN SCHEMA schema_name , schema_name FROM role_name , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), Parses("REVOKE SELECT , SELECT ON ALL SEQUENCES IN SCHEMA schema_name , schema_name FROM SESSION_USER , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name FROM role_name"), - Parses("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name FROM role_name , role_name"), - Parses("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM SESSION_USER , role_name"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name FROM CURRENT_USER , PUBLIC"), - Parses("REVOKE TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , PUBLIC"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , PUBLIC"), - Parses("REVOKE CREATE , TEMPORARY ON DATABASE database_name FROM role_name , CURRENT_USER"), - Parses("REVOKE TEMPORARY , CONNECT ON DATABASE database_name FROM SESSION_USER , SESSION_USER"), - Parses("REVOKE TEMP , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , SESSION_USER"), - Parses("REVOKE GRANT OPTION FOR TEMP , CREATE ON DATABASE database_name FROM PUBLIC GRANTED BY role_name"), - Parses("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY role_name"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , TEMP ON DATABASE database_name , database_name FROM SESSION_USER GRANTED BY role_name"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY role_name"), - Parses("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , PUBLIC GRANTED BY role_name"), - Parses("REVOKE CREATE ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name"), - Parses("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY role_name"), - Parses("REVOKE TEMPORARY ON DATABASE database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY role_name"), - Parses("REVOKE CONNECT , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_USER GRANTED BY role_name"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY role_name"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY role_name"), - Parses("REVOKE TEMP , CONNECT ON DATABASE database_name FROM role_name GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR TEMP , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("REVOKE TEMPORARY ON DATABASE database_name FROM CURRENT_ROLE , role_name GRANTED BY PUBLIC"), - Parses("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM CURRENT_USER , role_name GRANTED BY PUBLIC"), - Parses("REVOKE TEMPORARY ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER GRANTED BY PUBLIC"), - Parses("REVOKE TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("REVOKE ALL ON DATABASE database_name FROM CURRENT_USER GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR TEMP , CONNECT ON DATABASE database_name FROM SESSION_USER GRANTED BY CURRENT_ROLE"), - Parses("REVOKE TEMPORARY , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_ROLE"), - Parses("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY CURRENT_ROLE"), - Parses("REVOKE TEMP ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR TEMP , CREATE ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER GRANTED BY CURRENT_ROLE"), - Parses("REVOKE CREATE , TEMP ON DATABASE database_name FROM PUBLIC GRANTED BY CURRENT_USER"), - Parses("REVOKE TEMP , CREATE ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY CURRENT_USER"), - Parses("REVOKE CONNECT ON DATABASE database_name FROM role_name , role_name GRANTED BY CURRENT_USER"), - Parses("REVOKE CREATE , CREATE ON DATABASE database_name FROM PUBLIC , role_name GRANTED BY CURRENT_USER"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , role_name GRANTED BY CURRENT_USER"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_USER"), - Parses("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name FROM PUBLIC , PUBLIC GRANTED BY CURRENT_USER"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM PUBLIC , PUBLIC GRANTED BY CURRENT_USER"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_USER"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_USER"), - Parses("REVOKE CONNECT ON DATABASE database_name FROM PUBLIC , CURRENT_USER GRANTED BY CURRENT_USER"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON DATABASE database_name FROM CURRENT_USER GRANTED BY SESSION_USER"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM SESSION_USER GRANTED BY SESSION_USER"), - Parses("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM PUBLIC , role_name GRANTED BY SESSION_USER"), - Parses("REVOKE TEMP , CREATE ON DATABASE database_name FROM role_name , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name FROM role_name , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), - Parses("REVOKE TEMP , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM SESSION_USER CASCADE"), - Parses("REVOKE TEMP , CREATE ON DATABASE database_name FROM SESSION_USER , role_name CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , role_name CASCADE"), - Parses("REVOKE ALL ON DATABASE database_name FROM role_name , PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_USER CASCADE"), - Parses("REVOKE TEMPORARY , CREATE ON DATABASE database_name FROM CURRENT_USER , SESSION_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , SESSION_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY role_name CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY role_name CASCADE"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name CASCADE"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY role_name CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP ON DATABASE database_name , database_name FROM role_name , SESSION_USER GRANTED BY role_name CASCADE"), - Parses("REVOKE CREATE , CONNECT ON DATABASE database_name FROM PUBLIC , SESSION_USER GRANTED BY role_name CASCADE"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE CREATE , TEMP ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE TEMP , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name FROM SESSION_USER GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMPORARY ON DATABASE database_name FROM PUBLIC , PUBLIC GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMPORARY ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , SESSION_USER GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE TEMP , CREATE ON DATABASE database_name , database_name FROM role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP ON DATABASE database_name FROM PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE TEMP , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE ALL ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , TEMP ON DATABASE database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE TEMPORARY , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE TEMP , TEMP ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM role_name GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE TEMP , CREATE ON DATABASE database_name , database_name FROM role_name , PUBLIC GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CONNECT ON DATABASE database_name , database_name FROM role_name , PUBLIC GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM PUBLIC , PUBLIC GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE ALL ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMPORARY ON DATABASE database_name , database_name FROM SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE TEMP ON DATABASE database_name FROM role_name GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE CREATE , TEMP ON DATABASE database_name FROM PUBLIC GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE CONNECT , TEMP ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CONNECT , TEMPORARY ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE CONNECT ON DATABASE database_name FROM CURRENT_USER , SESSION_USER GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name FROM SESSION_USER , role_name RESTRICT"), - Parses("REVOKE TEMP , TEMP ON DATABASE database_name , database_name FROM PUBLIC , CURRENT_ROLE RESTRICT"), - Parses("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON DATABASE database_name FROM PUBLIC , SESSION_USER RESTRICT"), - Parses("REVOKE CONNECT , TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CONNECT , TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM role_name GRANTED BY role_name RESTRICT"), - Parses("REVOKE CREATE , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY role_name RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name RESTRICT"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM SESSION_USER , PUBLIC GRANTED BY role_name RESTRICT"), - Parses("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY role_name RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY role_name RESTRICT"), - Parses("REVOKE TEMP ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY role_name RESTRICT"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM role_name , SESSION_USER GRANTED BY role_name RESTRICT"), - Parses("REVOKE CREATE , CONNECT ON DATABASE database_name , database_name FROM role_name GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE CREATE , TEMP ON DATABASE database_name FROM SESSION_USER , PUBLIC GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON DATABASE database_name , database_name FROM SESSION_USER , PUBLIC GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM CURRENT_USER , SESSION_USER GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE ALL ON DATABASE database_name , database_name FROM CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CONNECT , TEMPORARY ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE TEMPORARY , CONNECT ON DATABASE database_name , database_name FROM role_name , PUBLIC GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , CONNECT ON DATABASE database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM role_name GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE CREATE ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE TEMP ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE TEMP ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM role_name , role_name GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM PUBLIC , role_name GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CONNECT , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE CONNECT , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , CONNECT ON DATABASE database_name , database_name FROM role_name , CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE CONNECT , TEMP ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE CREATE , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name FROM role_name"), + Converts("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name FROM role_name , role_name"), + Converts("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM SESSION_USER , role_name"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name FROM CURRENT_USER , PUBLIC"), + Converts("REVOKE TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , PUBLIC"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , PUBLIC"), + Converts("REVOKE CREATE , TEMPORARY ON DATABASE database_name FROM role_name , CURRENT_USER"), + Converts("REVOKE TEMPORARY , CONNECT ON DATABASE database_name FROM SESSION_USER , SESSION_USER"), + Converts("REVOKE TEMP , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , SESSION_USER"), + Converts("REVOKE GRANT OPTION FOR TEMP , CREATE ON DATABASE database_name FROM PUBLIC GRANTED BY role_name"), + Converts("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY role_name"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , TEMP ON DATABASE database_name , database_name FROM SESSION_USER GRANTED BY role_name"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY role_name"), + Converts("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , PUBLIC GRANTED BY role_name"), + Converts("REVOKE CREATE ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name"), + Converts("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY role_name"), + Converts("REVOKE TEMPORARY ON DATABASE database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY role_name"), + Converts("REVOKE CONNECT , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_USER GRANTED BY role_name"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY role_name"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY role_name"), + Converts("REVOKE TEMP , CONNECT ON DATABASE database_name FROM role_name GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR TEMP , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("REVOKE TEMPORARY ON DATABASE database_name FROM CURRENT_ROLE , role_name GRANTED BY PUBLIC"), + Converts("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM CURRENT_USER , role_name GRANTED BY PUBLIC"), + Converts("REVOKE TEMPORARY ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER GRANTED BY PUBLIC"), + Converts("REVOKE TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("REVOKE ALL ON DATABASE database_name FROM CURRENT_USER GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR TEMP , CONNECT ON DATABASE database_name FROM SESSION_USER GRANTED BY CURRENT_ROLE"), + Converts("REVOKE TEMPORARY , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_ROLE"), + Converts("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY CURRENT_ROLE"), + Converts("REVOKE TEMP ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR TEMP , CREATE ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER GRANTED BY CURRENT_ROLE"), + Converts("REVOKE CREATE , TEMP ON DATABASE database_name FROM PUBLIC GRANTED BY CURRENT_USER"), + Converts("REVOKE TEMP , CREATE ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY CURRENT_USER"), + Converts("REVOKE CONNECT ON DATABASE database_name FROM role_name , role_name GRANTED BY CURRENT_USER"), + Converts("REVOKE CREATE , CREATE ON DATABASE database_name FROM PUBLIC , role_name GRANTED BY CURRENT_USER"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , role_name GRANTED BY CURRENT_USER"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_USER"), + Converts("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name FROM PUBLIC , PUBLIC GRANTED BY CURRENT_USER"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM PUBLIC , PUBLIC GRANTED BY CURRENT_USER"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_USER"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_USER"), + Converts("REVOKE CONNECT ON DATABASE database_name FROM PUBLIC , CURRENT_USER GRANTED BY CURRENT_USER"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON DATABASE database_name FROM CURRENT_USER GRANTED BY SESSION_USER"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM SESSION_USER GRANTED BY SESSION_USER"), + Converts("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM PUBLIC , role_name GRANTED BY SESSION_USER"), + Converts("REVOKE TEMP , CREATE ON DATABASE database_name FROM role_name , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name FROM role_name , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY SESSION_USER"), + Converts("REVOKE TEMP , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM SESSION_USER CASCADE"), + Converts("REVOKE TEMP , CREATE ON DATABASE database_name FROM SESSION_USER , role_name CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , role_name CASCADE"), + Converts("REVOKE ALL ON DATABASE database_name FROM role_name , PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_USER CASCADE"), + Converts("REVOKE TEMPORARY , CREATE ON DATABASE database_name FROM CURRENT_USER , SESSION_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , SESSION_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY role_name CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY role_name CASCADE"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name CASCADE"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY role_name CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP ON DATABASE database_name , database_name FROM role_name , SESSION_USER GRANTED BY role_name CASCADE"), + Converts("REVOKE CREATE , CONNECT ON DATABASE database_name FROM PUBLIC , SESSION_USER GRANTED BY role_name CASCADE"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE CREATE , TEMP ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE TEMP , CONNECT ON DATABASE database_name , database_name FROM CURRENT_ROLE GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name FROM SESSION_USER GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMPORARY ON DATABASE database_name FROM PUBLIC , PUBLIC GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMPORARY ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , SESSION_USER GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE TEMP , CREATE ON DATABASE database_name , database_name FROM role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP ON DATABASE database_name FROM PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE TEMP , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE ALL ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , TEMP ON DATABASE database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE TEMPORARY , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE TEMP , TEMP ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name , database_name FROM role_name GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE TEMP , CREATE ON DATABASE database_name , database_name FROM role_name , PUBLIC GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CONNECT ON DATABASE database_name , database_name FROM role_name , PUBLIC GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM PUBLIC , PUBLIC GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE TEMPORARY , TEMP ON DATABASE database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , PUBLIC GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE ALL ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMPORARY ON DATABASE database_name , database_name FROM SESSION_USER , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE TEMP ON DATABASE database_name FROM role_name GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE CREATE , TEMP ON DATABASE database_name FROM PUBLIC GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE CONNECT , TEMP ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , CONNECT ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CONNECT , TEMPORARY ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE CONNECT ON DATABASE database_name FROM CURRENT_USER , SESSION_USER GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name FROM SESSION_USER , role_name RESTRICT"), + Converts("REVOKE TEMP , TEMP ON DATABASE database_name , database_name FROM PUBLIC , CURRENT_ROLE RESTRICT"), + Converts("REVOKE GRANT OPTION FOR ALL ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON DATABASE database_name FROM PUBLIC , SESSION_USER RESTRICT"), + Converts("REVOKE CONNECT , TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CONNECT , TEMP ON DATABASE database_name FROM PUBLIC , SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM role_name GRANTED BY role_name RESTRICT"), + Converts("REVOKE CREATE , CREATE ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY role_name RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name RESTRICT"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM SESSION_USER , PUBLIC GRANTED BY role_name RESTRICT"), + Converts("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM PUBLIC , CURRENT_ROLE GRANTED BY role_name RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY role_name RESTRICT"), + Converts("REVOKE TEMP ON DATABASE database_name FROM SESSION_USER , CURRENT_USER GRANTED BY role_name RESTRICT"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM role_name , SESSION_USER GRANTED BY role_name RESTRICT"), + Converts("REVOKE CREATE , CONNECT ON DATABASE database_name , database_name FROM role_name GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name , database_name FROM PUBLIC GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE CREATE , TEMP ON DATABASE database_name FROM SESSION_USER , PUBLIC GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON DATABASE database_name , database_name FROM SESSION_USER , PUBLIC GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE CREATE , TEMPORARY ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM CURRENT_USER , SESSION_USER GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE ALL ON DATABASE database_name , database_name FROM CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CONNECT , TEMPORARY ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE TEMPORARY , CONNECT ON DATABASE database_name , database_name FROM role_name , PUBLIC GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE TEMPORARY , TEMPORARY ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , CONNECT ON DATABASE database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CREATE ON DATABASE database_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM role_name GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR TEMP , TEMP ON DATABASE database_name , database_name FROM role_name , CURRENT_ROLE GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE CONNECT , CONNECT ON DATABASE database_name , database_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE CREATE ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE TEMP ON DATABASE database_name FROM CURRENT_ROLE GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE TEMP ON DATABASE database_name , database_name FROM CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , TEMP ON DATABASE database_name FROM role_name , role_name GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE ON DATABASE database_name FROM PUBLIC , role_name GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CONNECT , TEMPORARY ON DATABASE database_name , database_name FROM CURRENT_USER , role_name GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON DATABASE database_name FROM SESSION_USER , role_name GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE CONNECT , CREATE ON DATABASE database_name , database_name FROM SESSION_USER , role_name GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR TEMPORARY , CONNECT ON DATABASE database_name FROM CURRENT_ROLE , PUBLIC GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , CONNECT ON DATABASE database_name , database_name FROM role_name , CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE CONNECT , TEMP ON DATABASE database_name , database_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE CREATE , TEMP ON DATABASE database_name , database_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), Parses("REVOKE GRANT OPTION FOR USAGE ON DOMAIN domain_name , domain_name FROM CURRENT_USER , role_name"), Parses("REVOKE GRANT OPTION FOR ALL ON DOMAIN domain_name FROM CURRENT_ROLE , CURRENT_ROLE"), Parses("REVOKE ALL ON DOMAIN domain_name FROM PUBLIC , CURRENT_USER"), @@ -9913,64 +9913,64 @@ func TestRevoke(t *testing.T) { Parses("REVOKE ALL ON PARAMETER configuration_parameter , configuration_parameter FROM role_name GRANTED BY SESSION_USER RESTRICT"), Parses("REVOKE ALTER SYSTEM ON PARAMETER configuration_parameter , configuration_parameter FROM CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON PARAMETER configuration_parameter FROM CURRENT_ROLE , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name , schema_name FROM PUBLIC , SESSION_USER"), - Parses("REVOKE ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY role_name"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM CURRENT_USER , CURRENT_USER GRANTED BY role_name"), - Parses("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM PUBLIC , SESSION_USER GRANTED BY role_name"), - Parses("REVOKE ALL ON SCHEMA schema_name FROM role_name GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name FROM CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name FROM PUBLIC , PUBLIC GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name FROM PUBLIC , PUBLIC GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name , schema_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC"), - Parses("REVOKE ALL ON SCHEMA schema_name , schema_name FROM SESSION_USER , PUBLIC GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM role_name , CURRENT_ROLE GRANTED BY PUBLIC"), - Parses("REVOKE USAGE ON SCHEMA schema_name FROM role_name , CURRENT_USER GRANTED BY PUBLIC"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name FROM SESSION_USER GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_USER GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name , schema_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_ROLE"), - Parses("REVOKE GRANT OPTION FOR USAGE ON SCHEMA schema_name FROM role_name , PUBLIC GRANTED BY CURRENT_USER"), - Parses("REVOKE USAGE , USAGE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , role_name GRANTED BY SESSION_USER"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_USER GRANTED BY SESSION_USER"), - Parses("REVOKE CREATE , USAGE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY SESSION_USER"), - Parses("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name FROM PUBLIC , SESSION_USER GRANTED BY SESSION_USER"), - Parses("REVOKE USAGE , USAGE ON SCHEMA schema_name FROM SESSION_USER GRANTED BY role_name CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM role_name , role_name GRANTED BY role_name CASCADE"), - Parses("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_ROLE GRANTED BY role_name CASCADE"), - Parses("REVOKE ALL PRIVILEGES ON SCHEMA schema_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY role_name CASCADE"), - Parses("REVOKE CREATE , CREATE ON SCHEMA schema_name , schema_name FROM SESSION_USER , role_name GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM CURRENT_ROLE , PUBLIC GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , PUBLIC GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_USER GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name , schema_name FROM role_name , SESSION_USER GRANTED BY PUBLIC CASCADE"), - Parses("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name FROM role_name , role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM role_name , role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE USAGE , USAGE ON SCHEMA schema_name , schema_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE USAGE , CREATE ON SCHEMA schema_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY CURRENT_ROLE CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE ON SCHEMA schema_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE ALL ON SCHEMA schema_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE USAGE , USAGE ON SCHEMA schema_name FROM PUBLIC , CURRENT_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), - Parses("REVOKE ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM PUBLIC , role_name GRANTED BY SESSION_USER CASCADE"), - Parses("REVOKE ALL PRIVILEGES ON SCHEMA schema_name FROM CURRENT_USER , CURRENT_ROLE RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM SESSION_USER , SESSION_USER RESTRICT"), - Parses("REVOKE CREATE , CREATE ON SCHEMA schema_name FROM role_name , CURRENT_ROLE GRANTED BY role_name RESTRICT"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_ROLE GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE USAGE ON SCHEMA schema_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY PUBLIC RESTRICT"), - Parses("REVOKE GRANT OPTION FOR USAGE ON SCHEMA schema_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE CREATE , USAGE ON SCHEMA schema_name FROM role_name GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM PUBLIC GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM SESSION_USER , role_name GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name FROM PUBLIC , SESSION_USER GRANTED BY CURRENT_USER RESTRICT"), - Parses("REVOKE CREATE , USAGE ON SCHEMA schema_name FROM SESSION_USER , PUBLIC GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name , schema_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name , schema_name FROM PUBLIC , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), - Parses("REVOKE ALL ON SCHEMA schema_name , schema_name FROM SESSION_USER , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name , schema_name FROM PUBLIC , SESSION_USER"), + Converts("REVOKE ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY role_name"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM CURRENT_USER , CURRENT_USER GRANTED BY role_name"), + Converts("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM PUBLIC , SESSION_USER GRANTED BY role_name"), + Converts("REVOKE ALL ON SCHEMA schema_name FROM role_name GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name FROM CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name FROM PUBLIC , PUBLIC GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name FROM PUBLIC , PUBLIC GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name , schema_name FROM CURRENT_USER , PUBLIC GRANTED BY PUBLIC"), + Converts("REVOKE ALL ON SCHEMA schema_name , schema_name FROM SESSION_USER , PUBLIC GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM role_name , CURRENT_ROLE GRANTED BY PUBLIC"), + Converts("REVOKE USAGE ON SCHEMA schema_name FROM role_name , CURRENT_USER GRANTED BY PUBLIC"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name FROM SESSION_USER GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_USER GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name , schema_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_ROLE"), + Converts("REVOKE GRANT OPTION FOR USAGE ON SCHEMA schema_name FROM role_name , PUBLIC GRANTED BY CURRENT_USER"), + Converts("REVOKE USAGE , USAGE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , role_name GRANTED BY SESSION_USER"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_USER GRANTED BY SESSION_USER"), + Converts("REVOKE CREATE , USAGE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , CURRENT_USER GRANTED BY SESSION_USER"), + Converts("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name FROM PUBLIC , SESSION_USER GRANTED BY SESSION_USER"), + Converts("REVOKE USAGE , USAGE ON SCHEMA schema_name FROM SESSION_USER GRANTED BY role_name CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM role_name , role_name GRANTED BY role_name CASCADE"), + Converts("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_ROLE GRANTED BY role_name CASCADE"), + Converts("REVOKE ALL PRIVILEGES ON SCHEMA schema_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY role_name CASCADE"), + Converts("REVOKE CREATE , CREATE ON SCHEMA schema_name , schema_name FROM SESSION_USER , role_name GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM CURRENT_ROLE , PUBLIC GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , PUBLIC GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_USER GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name , schema_name FROM role_name , SESSION_USER GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE GRANT OPTION FOR USAGE , CREATE ON SCHEMA schema_name FROM role_name , role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM role_name , role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name FROM SESSION_USER , role_name GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE USAGE , USAGE ON SCHEMA schema_name , schema_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE USAGE , CREATE ON SCHEMA schema_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY CURRENT_ROLE CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE ON SCHEMA schema_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR CREATE , CREATE ON SCHEMA schema_name , schema_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE ALL ON SCHEMA schema_name FROM SESSION_USER , CURRENT_ROLE GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE USAGE , USAGE ON SCHEMA schema_name FROM PUBLIC , CURRENT_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name FROM CURRENT_ROLE , SESSION_USER GRANTED BY CURRENT_USER CASCADE"), + Converts("REVOKE ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM PUBLIC , role_name GRANTED BY SESSION_USER CASCADE"), + Converts("REVOKE ALL PRIVILEGES ON SCHEMA schema_name FROM CURRENT_USER , CURRENT_ROLE RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM SESSION_USER , SESSION_USER RESTRICT"), + Converts("REVOKE CREATE , CREATE ON SCHEMA schema_name FROM role_name , CURRENT_ROLE GRANTED BY role_name RESTRICT"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM PUBLIC , CURRENT_ROLE GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE USAGE ON SCHEMA schema_name FROM CURRENT_USER , CURRENT_ROLE GRANTED BY PUBLIC RESTRICT"), + Converts("REVOKE GRANT OPTION FOR USAGE ON SCHEMA schema_name FROM CURRENT_ROLE , CURRENT_ROLE GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM CURRENT_USER , SESSION_USER GRANTED BY CURRENT_ROLE RESTRICT"), + Converts("REVOKE CREATE , USAGE ON SCHEMA schema_name FROM role_name GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name FROM PUBLIC GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON SCHEMA schema_name , schema_name FROM SESSION_USER , role_name GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name FROM SESSION_USER , CURRENT_USER GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR ALL ON SCHEMA schema_name FROM PUBLIC , SESSION_USER GRANTED BY CURRENT_USER RESTRICT"), + Converts("REVOKE CREATE , USAGE ON SCHEMA schema_name FROM SESSION_USER , PUBLIC GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR USAGE , USAGE ON SCHEMA schema_name , schema_name FROM SESSION_USER , CURRENT_USER GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE GRANT OPTION FOR CREATE , USAGE ON SCHEMA schema_name , schema_name FROM PUBLIC , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), + Converts("REVOKE ALL ON SCHEMA schema_name , schema_name FROM SESSION_USER , SESSION_USER GRANTED BY SESSION_USER RESTRICT"), Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON TABLESPACE tablespace_name FROM PUBLIC"), Parses("REVOKE CREATE ON TABLESPACE tablespace_name , tablespace_name FROM CURRENT_USER"), Parses("REVOKE GRANT OPTION FOR ALL PRIVILEGES ON TABLESPACE tablespace_name FROM CURRENT_ROLE , role_name GRANTED BY role_name"), @@ -10012,12 +10012,12 @@ func TestRevoke(t *testing.T) { Parses("REVOKE ALL ON TYPE type_name , type_name FROM role_name GRANTED BY CURRENT_ROLE RESTRICT"), Parses("REVOKE GRANT OPTION FOR ALL ON TYPE type_name , type_name FROM CURRENT_ROLE , PUBLIC GRANTED BY CURRENT_ROLE RESTRICT"), Parses("REVOKE USAGE ON TYPE type_name FROM role_name , CURRENT_USER GRANTED BY CURRENT_ROLE RESTRICT"), - Parses("REVOKE ADMIN OPTION FOR role_name FROM CURRENT_USER"), - Parses("REVOKE ADMIN OPTION FOR role_name , role_name FROM role_name , SESSION_USER"), - Parses("REVOKE role_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name"), - Parses("REVOKE ADMIN OPTION FOR role_name FROM SESSION_USER , role_name GRANTED BY PUBLIC"), - Parses("REVOKE role_name , role_name FROM SESSION_USER GRANTED BY role_name CASCADE"), - Parses("REVOKE ADMIN OPTION FOR role_name FROM PUBLIC GRANTED BY PUBLIC CASCADE"), + Converts("REVOKE ADMIN OPTION FOR role_name FROM CURRENT_USER"), + Converts("REVOKE ADMIN OPTION FOR role_name , role_name FROM role_name , SESSION_USER"), + Converts("REVOKE role_name FROM CURRENT_ROLE , PUBLIC GRANTED BY role_name"), + Converts("REVOKE ADMIN OPTION FOR role_name FROM SESSION_USER , role_name GRANTED BY PUBLIC"), + Converts("REVOKE role_name , role_name FROM SESSION_USER GRANTED BY role_name CASCADE"), + Converts("REVOKE ADMIN OPTION FOR role_name FROM PUBLIC GRANTED BY PUBLIC CASCADE"), } RunTests(t, tests) } diff --git a/testing/generation/function_coverage/output/to_char_test.go b/testing/generation/function_coverage/output/to_char_test.go new file mode 100644 index 0000000000..6e6d1fd67c --- /dev/null +++ b/testing/generation/function_coverage/output/to_char_test.go @@ -0,0 +1,37 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package output + +import ( + "testing" + + "github.com/dolthub/go-mysql-server/sql" +) + +func Test_ToChar(t *testing.T) { + RunScripts(t, []ScriptTest{ + { + Name: "to_char", + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'YYYY-MM-DD HH24:MI:SS.MS');`, + Expected: []sql.Row{ + {"2021-09-15 21:43:56.123"}, + }, + }, + }, + }, + }) +} diff --git a/testing/go/auth_quick_test.go b/testing/go/auth_quick_test.go new file mode 100644 index 0000000000..a485f00b00 --- /dev/null +++ b/testing/go/auth_quick_test.go @@ -0,0 +1,365 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package _go + +import ( + "strings" + "testing" + + "github.com/dolthub/go-mysql-server/sql" +) + +// TestAuthQuick is modeled after the QuickPrivilegeTest in GMS, so please refer to the documentation there: +// https://github.com/dolthub/go-mysql-server/blob/main/enginetest/queries/priv_auth_queries.go +func TestAuthQuick(t *testing.T) { + // Statements that are run before every test (the state that all tests start with): + // CREATE USER tester PASSWORD 'password'; + // CREATE SCHEMA mysch; + // CREATE SCHEMA othersch; + // CREATE TABLE mysch.test (pk BIGINT PRIMARY KEY, v1 BIGINT); + // CREATE TABLE mysch.test2 (pk BIGINT PRIMARY KEY, v1 BIGINT); + // CREATE TABLE othersch.test (pk BIGINT PRIMARY KEY, v1 BIGINT); + // CREATE TABLE othersch.test2 (pk BIGINT PRIMARY KEY, v1 BIGINT); + // INSERT INTO mysch.test VALUES (0, 0), (1, 1); + // INSERT INTO mysch.test2 VALUES (0, 1), (1, 2); + // INSERT INTO othersch.test VALUES (1, 1), (2, 2); + // INSERT INTO othersch.test2 VALUES (1, 1), (2, 2); + type QuickPrivilegeTest struct { + Focus bool + Queries []string + Expected []sql.Row + ExpectedErr string + } + tests := []QuickPrivilegeTest{ + { + Queries: []string{ + "GRANT SELECT ON ALL TABLES IN SCHEMA mysch TO tester;", + "SELECT * FROM mysch.test;", + }, + Expected: []sql.Row{{0, 0}, {1, 1}}, + }, + { + Queries: []string{ + "GRANT SELECT ON ALL TABLES IN SCHEMA mysch TO tester;", + "SELECT * FROM mysch.test2;", + }, + Expected: []sql.Row{{0, 1}, {1, 2}}, + }, + { + Queries: []string{ + "GRANT SELECT ON mysch.test TO tester;", + "SELECT * FROM mysch.test;", + }, + Expected: []sql.Row{{0, 0}, {1, 1}}, + }, + { + Queries: []string{ + "GRANT SELECT ON mysch.test TO tester;", + "SELECT * FROM mysch.test2;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT SELECT ON ALL TABLES IN SCHEMA othersch TO tester;", + "SELECT * FROM mysch.test;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT SELECT ON othersch.test TO tester;", + "SELECT * FROM mysch.test;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT SELECT ON othersch.test TO tester;", + "SELECT * FROM mysch.test;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "CREATE SCHEMA newsch;", + }, + ExpectedErr: "permission denied for database", + }, + { + Queries: []string{ + "GRANT CREATE ON DATABASE postgres TO tester;", + "CREATE SCHEMA newsch;", + }, + }, + { // This isn't supported yet, but it is supposed to fail since tester is not an owner + Queries: []string{ + "GRANT CREATE ON DATABASE postgres TO tester;", + "CREATE SCHEMA newsch;", + "DROP SCHEMA newsch;", + }, + ExpectedErr: "not yet supported", + }, + { + Queries: []string{ + "CREATE TABLE mysch.new_table (pk BIGINT PRIMARY KEY);", + }, + ExpectedErr: "permission denied for schema", + }, + { + Queries: []string{ + "GRANT CREATE ON SCHEMA mysch TO tester;", + "CREATE TABLE mysch.new_table (pk BIGINT PRIMARY KEY);", + }, + }, + { + Queries: []string{ + "CREATE ROLE new_role;", + }, + ExpectedErr: "does not have permission", + }, + { + Queries: []string{ + "ALTER ROLE tester CREATEROLE;", + "CREATE ROLE new_role;", + }, + }, + { + Queries: []string{ + "CREATE USER new_user;", + }, + ExpectedErr: "does not have permission", + }, + { + Queries: []string{ + "ALTER ROLE tester SUPERUSER;", + "CREATE USER new_user;", + }, + }, + { + Queries: []string{ + "CREATE USER new_user;", + "DROP USER new_user;", + }, + ExpectedErr: "does not have permission", + }, + { + Queries: []string{ + "CREATE USER new_user;", + "ALTER ROLE tester CREATEROLE;", + "DROP USER new_user;", + }, + }, + { + Queries: []string{ + "CREATE USER new_user SUPERUSER;", + "ALTER ROLE tester CREATEROLE;", + "DROP USER new_user;", + }, + ExpectedErr: "does not have permission", + }, + { + Queries: []string{ + "CREATE USER new_user SUPERUSER;", + "ALTER ROLE tester SUPERUSER;", + "DROP USER new_user;", + }, + }, + { + Queries: []string{ + "DELETE FROM mysch.test WHERE pk >= 0;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT DELETE ON ALL TABLES IN SCHEMA mysch TO tester;", + "DELETE FROM mysch.test WHERE pk >= 0;", + }, + }, + { + Queries: []string{ + "GRANT DELETE ON mysch.test TO tester;", + "DELETE FROM mysch.test WHERE pk >= 0;", + }, + }, + { + Queries: []string{ + "CREATE USER tester2;", + "GRANT DELETE ON ALL TABLES IN SCHEMA mysch TO tester2;", + "GRANT tester2 TO tester;", + "DELETE FROM mysch.test WHERE pk >= 0;", + }, + }, + { + Queries: []string{ + "SELECT * FROM mysch.test JOIN mysch.test2 ON test.pk = test2.pk;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT SELECT ON mysch.test TO tester;", + "SELECT * FROM mysch.test JOIN mysch.test2 ON test.pk = test2.pk;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT SELECT ON mysch.test2 TO tester;", + "SELECT * FROM mysch.test JOIN mysch.test2 ON test.pk = test2.pk;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT SELECT ON mysch.test TO tester;", + "GRANT SELECT ON mysch.test2 TO tester;", + "SELECT * FROM mysch.test JOIN mysch.test2 ON test.pk = test2.pk;", + }, + Expected: []sql.Row{{0, 0, 0, 1}, {1, 1, 1, 2}}, + }, + { + Queries: []string{ + "CREATE USER tester2;", + "GRANT SELECT ON mysch.test2 TO tester2;", + "GRANT tester2 TO tester;", + "SELECT * FROM mysch.test JOIN mysch.test2 ON test.pk = test2.pk;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "CREATE USER tester2;", + "GRANT SELECT ON mysch.test TO tester2;", + "GRANT SELECT ON mysch.test2 TO tester2;", + "GRANT tester2 TO tester;", + "SELECT * FROM mysch.test JOIN mysch.test2 ON test.pk = test2.pk;", + }, + Expected: []sql.Row{{0, 0, 0, 1}, {1, 1, 1, 2}}, + }, + { + Queries: []string{ + "CREATE TABLE mysch.new_table (pk BIGINT PRIMARY KEY);", + "DROP TABLE mysch.new_table;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA mysch TO tester;", + "CREATE TABLE mysch.new_table (pk BIGINT PRIMARY KEY);", + "DROP TABLE mysch.new_table;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "CREATE TABLE mysch.new_table (pk BIGINT PRIMARY KEY);", + "GRANT postgres TO tester;", + "DROP TABLE mysch.new_table;", + }, + }, + { + Queries: []string{ + "CREATE ROLE new_role;", + "DROP ROLE new_role;", + }, + ExpectedErr: "does not have permission", + }, + { + Queries: []string{ + "ALTER ROLE tester CREATEROLE;", + "CREATE ROLE new_role;", + "DROP ROLE new_role;", + }, + }, + { + Queries: []string{ + "INSERT INTO mysch.test VALUES (9, 9);", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT INSERT ON ALL TABLES IN SCHEMA mysch TO tester;", + "INSERT INTO mysch.test VALUES (9, 9);", + }, + }, + { + Queries: []string{ + "GRANT INSERT ON mysch.test TO tester;", + "INSERT INTO mysch.test VALUES (9, 9);", + }, + }, + { + Queries: []string{ + "UPDATE mysch.test SET v1 = 0;", + }, + ExpectedErr: "permission denied for table", + }, + { + Queries: []string{ + "GRANT UPDATE ON ALL TABLES IN SCHEMA mysch TO tester;", + "UPDATE mysch.test SET v1 = 0;", + }, + }, + { + Queries: []string{ + "GRANT UPDATE ON mysch.test TO tester;", + "UPDATE mysch.test SET v1 = 0;", + }, + }, + } + // Here we'll convert each quick test into a standard test + scriptTests := make([]ScriptTest, len(tests)) + for testIdx, test := range tests { + scriptTests[testIdx] = ScriptTest{ + Name: strings.Join(test.Queries, "\n > "), + Database: "", + SetUpScript: []string{ + "CREATE USER tester PASSWORD 'password';", + "CREATE SCHEMA mysch;", + "CREATE SCHEMA othersch;", + "CREATE TABLE mysch.test (pk BIGINT PRIMARY KEY, v1 BIGINT);", + "CREATE TABLE mysch.test2 (pk BIGINT PRIMARY KEY, v1 BIGINT);", + "CREATE TABLE othersch.test (pk BIGINT PRIMARY KEY, v1 BIGINT);", + "CREATE TABLE othersch.test2 (pk BIGINT PRIMARY KEY, v1 BIGINT);", + "INSERT INTO mysch.test VALUES (0, 0), (1, 1);", + "INSERT INTO mysch.test2 VALUES (0, 1), (1, 2);", + "INSERT INTO othersch.test VALUES (1, 1), (2, 2);", + "INSERT INTO othersch.test2 VALUES (1, 1), (2, 2);", + }, + Assertions: make([]ScriptTestAssertion, len(test.Queries)), + Focus: test.Focus, + } + for queryIdx := 0; queryIdx < len(test.Queries)-1; queryIdx++ { + scriptTests[testIdx].Assertions[queryIdx] = ScriptTestAssertion{ + Query: test.Queries[queryIdx], + SkipResultsCheck: true, + Username: "postgres", + Password: "password", + } + } + scriptTests[testIdx].Assertions[len(test.Queries)-1] = ScriptTestAssertion{ + Query: test.Queries[len(test.Queries)-1], + Expected: test.Expected, + ExpectedErr: test.ExpectedErr, + Username: "tester", + Password: "password", + } + } + RunScripts(t, scriptTests) +} diff --git a/testing/go/auth_test.go b/testing/go/auth_test.go index 38b28b1dff..2a781a2815 100644 --- a/testing/go/auth_test.go +++ b/testing/go/auth_test.go @@ -351,6 +351,8 @@ func TestAuthTests(t *testing.T) { SetUpScript: []string{ `CREATE USER user1 PASSWORD 'a';`, `CREATE USER user2 PASSWORD 'b';`, + `GRANT ALL PRIVILEGES ON SCHEMA public TO user1;`, + `GRANT ALL PRIVILEGES ON SCHEMA public TO user2;`, }, Assertions: []ScriptTestAssertion{ { diff --git a/testing/go/benchmark/benchmark_folder.go b/testing/go/benchmark/benchmark_folder.go new file mode 100644 index 0000000000..6a217c0417 --- /dev/null +++ b/testing/go/benchmark/benchmark_folder.go @@ -0,0 +1,99 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "os" + "path/filepath" + "runtime" +) + +var benchmarkFolder BenchmarkFolderLocation // benchmarkFolder is the disk location of the benchmark folder + +// BenchmarkFolderLocation is the location of this project's root folder. +type BenchmarkFolderLocation struct { + path string +} + +// GetBenchmarkFolder returns the location of the benchmark folder (scripts/mini_benchmark). This is used to find the +// absolute position of our output files. +func GetBenchmarkFolder() (BenchmarkFolderLocation, error) { + _, currentFileLocation, _, ok := runtime.Caller(0) + if !ok { + return BenchmarkFolderLocation{}, fmt.Errorf("failed to fetch the location of the current file") + } + benchmarkFolder = BenchmarkFolderLocation{filepath.Clean(filepath.Join(filepath.Dir(currentFileLocation), + "../../../scripts/mini_sysbench"))} + return benchmarkFolder, nil +} + +// MoveRoot returns a new BenchmarkFolderLocation that defines the root at the new path. The parameter should be +// relative to the current root. +func (root BenchmarkFolderLocation) MoveRoot(relativePath string) BenchmarkFolderLocation { + return BenchmarkFolderLocation{filepath.Clean(filepath.Join(root.path, relativePath))} +} + +// GetAbsolutePath returns the absolute path of the given path, which should be relative to the project's root +// folder. +func (root BenchmarkFolderLocation) GetAbsolutePath(relativePath string) string { + return filepath.ToSlash(filepath.Join(root.path, relativePath)) +} + +// Exists returns whether the file or directory at the given path (relative to the root path) exists. Returns an error +// if the check was unable to be completed. +func (root BenchmarkFolderLocation) Exists(relativePath string) (bool, error) { + _, err := os.Stat(root.GetAbsolutePath(relativePath)) + if os.IsNotExist(err) { + return false, nil + } else if err != nil { + return false, err + } + return true, nil +} + +// ReadDir is equivalent to os.ReadDir, except that it uses the root path and the given relative path. +func (root BenchmarkFolderLocation) ReadDir(relativePath string) ([]os.DirEntry, error) { + return os.ReadDir(root.GetAbsolutePath(relativePath)) +} + +// ReadFile is equivalent to os.ReadFile, except that it uses the root path and the given relative path. +func (root BenchmarkFolderLocation) ReadFile(relativePath string) ([]byte, error) { + return os.ReadFile(root.GetAbsolutePath(relativePath)) +} + +// WriteFile is equivalent to os.WriteFile, except that it uses the root path and the given relative path. +func (root BenchmarkFolderLocation) WriteFile(relativePath string, data []byte, perm os.FileMode) error { + directory := filepath.ToSlash(filepath.Dir(relativePath)) + exists, err := root.Exists(directory) + if err != nil { + return err + } + if !exists { + if err = os.MkdirAll(root.GetAbsolutePath(directory), 0644); err != nil { + return err + } + } + return os.WriteFile(root.GetAbsolutePath(relativePath), data, perm) +} + +// init is used to load the location of the benchmark folder. +func init() { + var err error + benchmarkFolder, err = GetBenchmarkFolder() + if err != nil { + panic(err) + } +} diff --git a/testing/go/benchmark/main.go b/testing/go/benchmark/main.go new file mode 100644 index 0000000000..b5aa8d246b --- /dev/null +++ b/testing/go/benchmark/main.go @@ -0,0 +1,73 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "fmt" + "math" + "os" + "strings" +) + +// AllowedVariance represents the amount that must change before a result is noteworthy. The number represents a whole +// percentage, so "10" equals "10%". +const AllowedVariance = 10 + +// main analyzes two separate runs of scripts/quick_sysbench.sh, and creates a table comparing the differences. This +// table is intended for display in a GitHub PR. +func main() { + prBenchmark, err := benchmarkFolder.ReadFile("results1.log") + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + mainBenchmark, err := benchmarkFolder.ReadFile("results2.log") + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + prSections := SectionResults(string(prBenchmark)) + mainSections := SectionResults(string(mainBenchmark)) + sb := strings.Builder{} + sb.WriteString("| | Main | PR | |\n") + sb.WriteString("| --- | --- | --- | --- |\n") + for _, kv := range GetMapKVsSorted(mainSections) { + mainSection := kv.Value + prSection, ok := prSections[mainSection.Test] + if !ok { + sb.WriteString(fmt.Sprintf("| %s | %.2f/s | ${\\color{red}DNF}$ | |\n", + mainSection.Test, mainSection.Time)) + continue + } + percentChange := math.Floor(((prSection.Time/mainSection.Time)-1.0)*1000.0) / 10.0 + if percentChange > AllowedVariance { // Greatly positive + sb.WriteString(fmt.Sprintf("| %s | %.2f/s | ${\\color{lightgreen}%.2f/s}$ | ${\\color{lightgreen}+%.1f\\\\%%}$ |\n", + mainSection.Test, mainSection.Time, prSection.Time, percentChange)) + } else if percentChange < -AllowedVariance { // Greatly negative + sb.WriteString(fmt.Sprintf("| %s | %.2f/s | ${\\color{red}%.2f/s}$ | ${\\color{red}%.1f\\\\%%}$ |\n", + mainSection.Test, mainSection.Time, prSection.Time, percentChange)) + } else if percentChange > 0 { // Positive + sb.WriteString(fmt.Sprintf("| %s | %.2f/s | %.2f/s | +%.1f%% |\n", + mainSection.Test, mainSection.Time, prSection.Time, percentChange)) + } else if percentChange < 0 { // Negative + sb.WriteString(fmt.Sprintf("| %s | %.2f/s | %.2f/s | %.1f%% |\n", + mainSection.Test, mainSection.Time, prSection.Time, percentChange)) + } else { // No Change + sb.WriteString(fmt.Sprintf("| %s | %.2f/s | %.2f/s | 0.0%% |\n", + mainSection.Test, mainSection.Time, prSection.Time)) + } + } + fmt.Println(sb.String()) +} diff --git a/testing/go/benchmark/map.go b/testing/go/benchmark/map.go new file mode 100644 index 0000000000..99f5bc98eb --- /dev/null +++ b/testing/go/benchmark/map.go @@ -0,0 +1,46 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "cmp" + "sort" +) + +// KeyValue represents an entry in a map. +type KeyValue[K comparable, V any] struct { + Key K + Value V +} + +// GetMapKVs returns the map's KeyValue entries as an unsorted slice. +func GetMapKVs[K comparable, V any](m map[K]V) []KeyValue[K, V] { + allEntries := make([]KeyValue[K, V], len(m)) + i := 0 + for k, v := range m { + allEntries[i] = KeyValue[K, V]{Key: k, Value: v} + i++ + } + return allEntries +} + +// GetMapKVsSorted returns the map's KeyValue entries as a sorted slice. The keys are sorted in ascending order. +func GetMapKVsSorted[K cmp.Ordered, V any](m map[K]V) []KeyValue[K, V] { + allEntries := GetMapKVs(m) + sort.Slice(allEntries, func(i, j int) bool { + return allEntries[i].Key < allEntries[j].Key + }) + return allEntries +} diff --git a/testing/go/benchmark/section.go b/testing/go/benchmark/section.go new file mode 100644 index 0000000000..a7008c05bd --- /dev/null +++ b/testing/go/benchmark/section.go @@ -0,0 +1,71 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "strconv" + "strings" +) + +// Section represents a benchmark section. +type Section struct { + Test string + Time float64 // This is in number of iterations per second +} + +// SectionResults creates a section for each test. +func SectionResults(fileData string) map[string]Section { + sections := make(map[string]Section) + for { + headerStartIdx := strings.Index(fileData, `----`) + if headerStartIdx == -1 { + break + } + headerEndIdx := strings.Index(fileData[headerStartIdx+4:], `----`) + headerStartIdx + 4 + if headerEndIdx == headerStartIdx+4 { + break + } + headerFull := fileData[headerStartIdx : headerEndIdx+4] + endingHeaderIdx := strings.LastIndex(fileData, headerFull) + if endingHeaderIdx == -1 { + break + } + section := Section{ + Test: headerFull[4 : len(headerFull)-4], + Time: -1, + } + sectionText := strings.TrimSpace(fileData[len(headerFull):endingHeaderIdx]) + fileData = fileData[endingHeaderIdx+len(headerFull):] + for _, line := range strings.Split(sectionText, "\n") { + if strings.Contains(line, `queries:`) { + parenIdx := strings.Index(line, `(`) + perSecIdx := strings.Index(line, ` per sec.)`) + if parenIdx != -1 && perSecIdx != -1 { + timeString := line[parenIdx+1 : perSecIdx] + parsedTime, err := strconv.ParseFloat(timeString, 64) + if err == nil { + section.Time = parsedTime + } + } + break + } + } + if section.Time == -1 { + continue + } + sections[section.Test] = section + } + return sections +} diff --git a/testing/go/dolt_tables_test.go b/testing/go/dolt_tables_test.go index b1533be026..fba69eb0eb 100755 --- a/testing/go/dolt_tables_test.go +++ b/testing/go/dolt_tables_test.go @@ -954,6 +954,152 @@ func TestUserSpaceDoltTables(t *testing.T) { }, }, }, + { + Name: "dolt docs", + SetUpScript: []string{ + "INSERT INTO dolt.docs values ('README.md', 'testing')", + }, + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT * FROM dolt.docs`, + Expected: []sql.Row{ + {"README.md", "testing"}, + }, + }, + { + Query: `SELECT * FROM dolt_docs`, + Expected: []sql.Row{ + {"README.md", "testing"}, + }, + }, + { + Skip: true, // TODO: referencing items outside the schema or database is not yet supported + Query: `SELECT dolt.docs.doc_name FROM dolt.docs`, + Expected: []sql.Row{{"README.md"}}, + }, + { + Skip: true, // TODO: table not found: dolt_docs + Query: `SELECT dolt_docs.doc_name FROM dolt_docs`, + Expected: []sql.Row{{"README.md"}}, + }, + { + Query: `SELECT * FROM public.docs`, + ExpectedErr: "table not found", + }, + { + Query: `SELECT * FROM docs`, + ExpectedErr: "table not found", + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING')`, + Expected: []sql.Row{ + {"", "dolt.docs", "added", 1, 1}, + }, + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'docs')`, + Expected: []sql.Row{ + {"", "dolt.docs", "added", 1, 1}, + }, + }, + { + Skip: true, // TODO: we should support this + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'dolt_docs')`, + Expected: []sql.Row{ + {"", "dolt_docs", "added", 1, 1}, + }, + }, + { + Skip: true, // TODO: we should support this or a --schema flag + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'dolt.docs')`, + Expected: []sql.Row{ + {"", "dolt.docs", "added", 1, 1}, + }, + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'docs')`, + Expected: []sql.Row{ + {"", "dolt.docs", "added", 1, 1}, + }, + }, + { + Query: `SELECT diff_type, from_doc_name, to_doc_name FROM dolt_diff('main', 'WORKING', 'docs')`, + Expected: []sql.Row{ + {"added", nil, "README.md"}, + }, + }, + { + Query: `SELECT diff_type, from_doc_name, to_doc_name FROM dolt_diff('main', 'WORKING', 'docs')`, + Expected: []sql.Row{ + {"added", nil, "README.md"}, + }, + }, + { + Query: `CREATE TABLE docs (id INT PRIMARY KEY)`, + Expected: []sql.Row{}, + }, + { + Query: `INSERT INTO docs VALUES (1)`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM docs`, + Expected: []sql.Row{{1}}, + }, + { + Query: `SELECT doc_name FROM dolt.docs`, + Expected: []sql.Row{{"README.md"}}, + }, + { + Query: "SET search_path = 'dolt'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT doc_name FROM docs`, + Expected: []sql.Row{{"README.md"}}, + }, + { + Query: `SELECT * FROM public.docs`, + Expected: []sql.Row{{1}}, + }, + { + Query: "SET search_path = 'public'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM docs`, + Expected: []sql.Row{{1}}, + }, + { + Query: "SET search_path = 'public,dolt'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM docs`, + Expected: []sql.Row{{1}}, + }, + { + Query: `SELECT * FROM DOCS`, + Expected: []sql.Row{{1}}, + }, + { + Query: "SET search_path = 'public'", + Expected: []sql.Row{}, + }, + { + Query: `DELETE FROM dolt.docs WHERE doc_name = 'README.md'`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM dolt.docs`, + Expected: []sql.Row{}, + }, + { + Query: `DELETE FROM dolt_docs WHERE doc_name = 'README.md'`, + Expected: []sql.Row{}, + }, + }, + }, { Name: "dolt diff", SetUpScript: []string{ @@ -1272,6 +1418,175 @@ func TestUserSpaceDoltTables(t *testing.T) { }, }, }, + { + Name: "dolt ignore", + SetUpScript: []string{}, + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT * FROM dolt_ignore`, + Expected: []sql.Row{}, + }, + { + Query: "INSERT INTO dolt_ignore VALUES ('generated_*', true), ('generated_exception', false)", + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM dolt_ignore`, + Expected: []sql.Row{ + {"generated_*", "t"}, + {"generated_exception", "f"}, + }, + }, + { + Query: `SELECT * FROM public.dolt_ignore`, + Expected: []sql.Row{ + {"generated_*", "t"}, + {"generated_exception", "f"}, + }, + }, + { + Query: `SELECT dolt_ignore.pattern FROM public.dolt_ignore`, + Expected: []sql.Row{ + {"generated_*"}, + {"generated_exception"}, + }, + }, + { + Query: `SELECT name FROM other.dolt_ignore`, + ExpectedErr: "database schema not found", + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING')`, + Expected: []sql.Row{ + {"", "public.dolt_ignore", "added", 1, 1}, + }, + }, + { + Query: `SELECT diff_type, from_pattern, to_pattern FROM dolt_diff('main', 'WORKING', 'dolt_ignore')`, + Expected: []sql.Row{ + {"added", nil, "generated_*"}, + {"added", nil, "generated_exception"}, + }, + }, + { + Query: "CREATE TABLE foo (pk int);", + Expected: []sql.Row{}, + }, + { + Query: "CREATE TABLE generated_foo (pk int);", + Expected: []sql.Row{}, + }, + { + Query: "CREATE TABLE generated_exception (pk int);", + Expected: []sql.Row{}, + }, + { + Query: "SELECT dolt_add('-A');", + Expected: []sql.Row{{"{0}"}}, + }, + { + Query: "SELECT * FROM dolt_status;", + Expected: []sql.Row{ + {"public.dolt_ignore", 1, "new table"}, + {"public.foo", 1, "new table"}, + {"public.generated_exception", 1, "new table"}, + {"public.generated_foo", 0, "new table"}, + }, + }, + { + Query: `CREATE SCHEMA newschema`, + Expected: []sql.Row{}, + }, + { + Query: "INSERT INTO newschema.dolt_ignore VALUES ('test_*', true)", + Expected: []sql.Row{}, + }, + { + Query: "SET search_path = 'newschema'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM dolt_ignore`, + Expected: []sql.Row{ + {"test_*", "t"}, + }, + }, + { + // Should ignore generated_expected table in newschema but not in public + Query: "INSERT INTO dolt_ignore VALUES ('generated_exception', true)", + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM dolt_ignore`, + Expected: []sql.Row{ + {"generated_exception", "t"}, + {"test_*", "t"}, + }, + }, + { + Query: `SELECT * FROM newschema.dolt_ignore`, + Expected: []sql.Row{ + {"generated_exception", "t"}, + {"test_*", "t"}, + }, + }, + { + Query: `SELECT * FROM public.dolt_ignore`, + Expected: []sql.Row{ + {"generated_*", "t"}, + {"generated_exception", "f"}, + }, + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'dolt_ignore')`, + Expected: []sql.Row{ + {"", "newschema.dolt_ignore", "added", 1, 1}, + }, + }, + { + Query: `SELECT pattern FROM public.dolt_ignore`, + Expected: []sql.Row{ + {"generated_*"}, + {"generated_exception"}, + }, + }, + { + Query: "CREATE TABLE foo (pk int);", + Expected: []sql.Row{}, + }, + { + Query: "CREATE TABLE test_foo (pk int);", + Expected: []sql.Row{}, + }, + { + Query: "CREATE TABLE generated_foo (pk int);", + Expected: []sql.Row{}, + }, + { + Query: "CREATE TABLE generated_exception (pk int);", + Expected: []sql.Row{}, + }, + { + Query: "SELECT dolt_add('-A');", + Expected: []sql.Row{{"{0}"}}, + }, + { + Query: "SELECT * FROM dolt_status ORDER BY table_name;", + Expected: []sql.Row{ + {"newschema", 1, "new schema"}, + {"newschema.dolt_ignore", 1, "new table"}, + {"newschema.foo", 1, "new table"}, + {"newschema.generated_exception", 0, "new table"}, + {"newschema.generated_foo", 1, "new table"}, + {"newschema.test_foo", 0, "new table"}, + {"public.dolt_ignore", 1, "new table"}, + {"public.foo", 1, "new table"}, + {"public.generated_exception", 1, "new table"}, + {"public.generated_foo", 0, "new table"}, + }, + }, + }, + }, { Name: "dolt log", Assertions: []ScriptTestAssertion{ @@ -1493,67 +1808,175 @@ func TestUserSpaceDoltTables(t *testing.T) { }, }, { - Name: "dolt docs", + Name: "dolt procedures", SetUpScript: []string{ - "INSERT INTO dolt.docs values ('README.md', 'testing')", + // TODO: Create procedure when supported }, Assertions: []ScriptTestAssertion{ { - Query: `SELECT * FROM dolt.docs`, + Query: `SELECT * FROM dolt_procedures`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM public.dolt_procedures`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT dolt_procedures.name FROM public.dolt_procedures`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM other.dolt_procedures`, + ExpectedErr: "database schema not found", + }, + // TODO: Add diff tests when create procedure works + { + Query: `CREATE SCHEMA newschema`, + Expected: []sql.Row{}, + }, + { + Query: "SET search_path = 'newschema'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM newschema.dolt_procedures`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM dolt_procedures`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM public.dolt_procedures`, + Expected: []sql.Row{}, + }, + { + Query: "SET search_path = 'newschema,public'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM dolt_procedures`, + Expected: []sql.Row{}, + }, + }, + }, + { + Name: "dolt rebase", + SetUpScript: []string{ + // create a simple table + "create table t (pk int primary key);", + "select dolt_commit('-Am', 'creating table t');", + + // create a new branch that we'll add more commits to later + "select dolt_branch('branch1');", + + // create another commit on the main branch, right after where branch1 branched off + "insert into t values (0);", + "select dolt_commit('-am', 'inserting row 0');", + + // switch to branch1 and create three more commits that each insert one row + "select dolt_checkout('branch1');", + "insert into t values (1);", + "select dolt_commit('-am', 'inserting row 1');", + "insert into t values (2);", + "select dolt_commit('-am', 'inserting row 2');", + "insert into t values (3);", + "select dolt_commit('-am', 'inserting row 3');", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select message from dolt_log;", Expected: []sql.Row{ - {"README.md", "testing"}, + {"inserting row 3"}, + {"inserting row 2"}, + {"inserting row 1"}, + {"creating table t"}, + {"CREATE DATABASE"}, + {"Initialize data repository"}, }, }, { - Query: `SELECT * FROM dolt_docs`, + Query: `select dolt_rebase('-i', 'main');`, + Expected: []sql.Row{{"{0,\"interactive rebase started on branch dolt_rebase_branch1; adjust the rebase plan in the dolt_rebase table, then continue rebasing by calling dolt_rebase('--continue')\"}"}}, + }, + { + Query: "select rebase_order, action, commit_message from dolt_rebase order by rebase_order;", Expected: []sql.Row{ - {"README.md", "testing"}, + {float64(1), "pick", "inserting row 1"}, + {float64(2), "pick", "inserting row 2"}, + {float64(3), "pick", "inserting row 3"}, }, }, { - Skip: true, // TODO: referencing items outside the schema or database is not yet supported - Query: `SELECT dolt.docs.doc_name FROM dolt.docs`, - Expected: []sql.Row{{"README.md"}}, + Query: "select rebase_order, action, commit_message from dolt.rebase order by rebase_order;", + Expected: []sql.Row{ + {float64(1), "pick", "inserting row 1"}, + {float64(2), "pick", "inserting row 2"}, + {float64(3), "pick", "inserting row 3"}, + }, }, { - Skip: true, // TODO: table not found: dolt_docs - Query: `SELECT dolt_docs.doc_name FROM dolt_docs`, - Expected: []sql.Row{{"README.md"}}, + Query: "select rebase.commit_message from dolt.rebase order by rebase_order;", + Expected: []sql.Row{ + {"inserting row 1"}, + {"inserting row 2"}, + {"inserting row 3"}, + }, }, { - Query: `SELECT * FROM public.docs`, + Skip: true, // TODO: table not found: dolt_rebase + Query: "select dolt_rebase.commit_message from dolt_rebase order by rebase_order;", + Expected: []sql.Row{ + {"inserting row 1"}, + {"inserting row 2"}, + {"inserting row 3"}, + }, + }, + { + Query: `SELECT * FROM public.rebase`, ExpectedErr: "table not found", }, { - Query: `SELECT * FROM docs`, + Query: `SELECT * FROM rebase`, ExpectedErr: "table not found", }, { - Query: `CREATE TABLE docs (id INT PRIMARY KEY)`, + Query: `CREATE TABLE rebase (id INT PRIMARY KEY)`, Expected: []sql.Row{}, }, { - Query: `INSERT INTO docs VALUES (1)`, + Query: `INSERT INTO rebase VALUES (1)`, Expected: []sql.Row{}, }, { - Query: `SELECT * FROM docs`, + Query: `SELECT * FROM rebase`, Expected: []sql.Row{{1}}, }, { - Query: `SELECT doc_name FROM dolt.docs`, - Expected: []sql.Row{{"README.md"}}, + Query: `SELECT commit_message FROM dolt.rebase`, + Expected: []sql.Row{ + {"inserting row 1"}, + {"inserting row 2"}, + {"inserting row 3"}, + }, + }, + { + Query: `CREATE SCHEMA dolt`, + ExpectedErr: "schema exists", }, { Query: "SET search_path = 'dolt'", Expected: []sql.Row{}, }, { - Query: `SELECT doc_name FROM docs`, - Expected: []sql.Row{{"README.md"}}, + Query: `SELECT commit_message FROM rebase`, + Expected: []sql.Row{ + {"inserting row 1"}, + {"inserting row 2"}, + {"inserting row 3"}}, }, { - Query: `SELECT * FROM public.docs`, + Query: `SELECT * FROM public.rebase`, Expected: []sql.Row{{1}}, }, { @@ -1561,7 +1984,7 @@ func TestUserSpaceDoltTables(t *testing.T) { Expected: []sql.Row{}, }, { - Query: `SELECT * FROM docs`, + Query: `SELECT * FROM rebase`, Expected: []sql.Row{{1}}, }, { @@ -1569,13 +1992,68 @@ func TestUserSpaceDoltTables(t *testing.T) { Expected: []sql.Row{}, }, { - Query: `SELECT * FROM docs`, + Query: `SELECT * FROM rebase`, Expected: []sql.Row{{1}}, }, { - Query: `SELECT * FROM DOCS`, + Query: `SELECT * FROM REBASE`, Expected: []sql.Row{{1}}, }, + { + // Remove created table so we can continue with the rebase + Query: `DROP TABLE public.rebase;`, + Expected: []sql.Row{}, + }, + { + Query: "update dolt.rebase set action='reword', commit_message='insert rows' where rebase_order=1;", + Expected: []sql.Row{}, + }, + { + Query: "update dolt.rebase set action='drop' where rebase_order=2;", + Expected: []sql.Row{}, + }, + { + Query: "update dolt_rebase set action='fixup' where rebase_order=3;", + Expected: []sql.Row{}, + }, + { + Query: "select rebase_order, action, commit_message from dolt_rebase order by rebase_order;", + Expected: []sql.Row{ + {float64(1), "reword", "insert rows"}, + {float64(2), "drop", "inserting row 2"}, + {float64(3), "fixup", "inserting row 3"}, + }, + }, + { + Query: "select rebase_order, action, commit_message from dolt.rebase order by rebase_order;", + Expected: []sql.Row{ + {float64(1), "reword", "insert rows"}, + {float64(2), "drop", "inserting row 2"}, + {float64(3), "fixup", "inserting row 3"}, + }, + }, + { + Query: "select dolt_rebase('--continue');", + Expected: []sql.Row{{"{0,\"Successfully rebased and updated refs/heads/branch1\"}"}}, + }, + { + Query: "select message from dolt_log;", + Expected: []sql.Row{ + {"insert rows"}, + {"inserting row 0"}, + {"creating table t"}, + {"CREATE DATABASE"}, + {"Initialize data repository"}, + }, + }, + { + Query: "select * from dolt_rebase;", + ExpectedErr: "table not found: dolt_rebase", + }, + { + Query: "select * from dolt.rebase;", + ExpectedErr: "table not found: rebase", + }, }, }, { @@ -1831,6 +2309,7 @@ func TestUserSpaceDoltTables(t *testing.T) { Name: "dolt schemas", SetUpScript: []string{ "create view myView as select 2 + 2", + // TODO: Add more tests when triggers and events work in doltgres }, Assertions: []ScriptTestAssertion{ { @@ -1845,6 +2324,201 @@ func TestUserSpaceDoltTables(t *testing.T) { }, }, }, + { + Query: `SELECT * FROM public.dolt_schemas`, + Expected: []sql.Row{ + { + "view", + "myview", + "create view myView as select 2 + 2", + "{\"CreatedAt\":0}", + "NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES", + }, + }, + }, + { + Query: `SELECT dolt_schemas.name FROM public.dolt_schemas`, + Expected: []sql.Row{{"myview"}}, + }, + { + Query: `SELECT * FROM public.myview`, + Expected: []sql.Row{{4}}, + }, + { + Query: `SELECT name FROM other.dolt_schemas`, + ExpectedErr: "database schema not found", + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING')`, + Expected: []sql.Row{ + {"", "public.dolt_schemas", "added", 1, 1}, + }, + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'dolt_schemas')`, + Expected: []sql.Row{ + {"", "public.dolt_schemas", "added", 1, 1}, + }, + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'dolt_schemas')`, + Expected: []sql.Row{ + {"", "public.dolt_schemas", "added", 1, 1}, + }, + }, + { + Query: `SELECT diff_type, from_name, to_name FROM dolt_diff('main', 'WORKING', 'dolt_schemas')`, + Expected: []sql.Row{ + {"added", nil, "myview"}, + }, + }, + { + Query: `SELECT diff_type, from_name, to_name FROM dolt_diff('main', 'WORKING', 'dolt_schemas')`, + Expected: []sql.Row{ + {"added", nil, "myview"}, + }, + }, + { + Query: `CREATE SCHEMA newschema`, + Expected: []sql.Row{}, + }, + { + Query: "SET search_path = 'newschema'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM myview`, + ExpectedErr: "table not found: myview", + }, + { + Query: `SELECT * FROM public.myview`, + Expected: []sql.Row{{4}}, + }, + { + Query: `CREATE VIEW testView AS SELECT 1 + 1`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM newschema.dolt_schemas`, + Expected: []sql.Row{ + { + "view", + "testview", + "CREATE VIEW testView AS SELECT 1 + 1", + "{\"CreatedAt\":0}", + "NO_ENGINE_SUBSTITUTION,ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES", + }, + }, + }, + { + Query: `SELECT name FROM dolt_schemas`, + Expected: []sql.Row{{"testview"}}, + }, + { + Query: "SELECT table_schema, table_name FROM information_schema.views", + Expected: []sql.Row{ + {"newschema", "testview"}, + {"public", "myview"}, + }, + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'dolt_schemas')`, + Expected: []sql.Row{ + {"", "newschema.dolt_schemas", "added", 1, 1}, + }, + }, + { + Skip: true, // TODO: Should be able to specify schema + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'public.dolt_schemas')`, + Expected: []sql.Row{ + {"", "public.dolt_schemas", "added", 1, 1}, + }, + }, + { + Query: `SELECT name FROM public.dolt_schemas`, + Expected: []sql.Row{{"myview"}}, + }, + { + Query: "DROP VIEW myView", + ExpectedErr: "the view postgres.myview does not exist", + }, + { + Query: "DROP VIEW public.myView", + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM public.dolt_schemas`, + Expected: []sql.Row{}, + }, + { + Query: "create view public.myNewView as select 3 + 3", + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM public.dolt_schemas`, + Expected: []sql.Row{{"mynewview"}}, + }, + { + Query: `SELECT name FROM dolt_schemas`, + Expected: []sql.Row{{"testview"}}, + }, + { + Query: "SET search_path = 'newschema,public'", + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM dolt_schemas`, + Expected: []sql.Row{{"testview"}}, + }, + { + Query: `SELECT * FROM dolt_diff_summary('main', 'WORKING', 'dolt_schemas')`, + Expected: []sql.Row{ + {"", "newschema.dolt_schemas", "added", 1, 1}, + }, + }, + // Test same view name on different schemas + { + Query: "SET search_path = 'public'", + Expected: []sql.Row{}, + }, + { + Query: `CREATE VIEW testView AS SELECT 4 + 4`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT name, fragment FROM dolt_schemas`, + Expected: []sql.Row{ + {"mynewview", "create view public.myNewView as select 3 + 3"}, + {"testview", "CREATE VIEW testView AS SELECT 4 + 4"}, + }, + }, + { + Query: `SELECT name, fragment FROM newschema.dolt_schemas`, + Expected: []sql.Row{{"testview", "CREATE VIEW testView AS SELECT 1 + 1"}}, + }, + { + Query: `SELECT name, fragment FROM dolt_schemas`, + Expected: []sql.Row{ + {"mynewview", "create view public.myNewView as select 3 + 3"}, + {"testview", "CREATE VIEW testView AS SELECT 4 + 4"}, + }, + }, + { + Query: "DROP VIEW IF EXISTS noexist.testView", + Expected: []sql.Row{}, + }, + { + Query: "DROP VIEW IF EXISTS newschema.testView", + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM newschema.dolt_schemas`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT name FROM dolt_schemas`, + Expected: []sql.Row{{"mynewview"}, {"testview"}}, + }, }, }, { diff --git a/testing/go/functions_test.go b/testing/go/functions_test.go index 2b7c7ecb53..34e60d0962 100644 --- a/testing/go/functions_test.go +++ b/testing/go/functions_test.go @@ -1893,3 +1893,80 @@ func TestStringFunction(t *testing.T) { }, }) } + +func TestFormatFunctions(t *testing.T) { + RunScripts(t, []ScriptTest{ + { + Name: "test to_char", + SetUpScript: []string{}, + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'YYYY-MM-DD HH24:MI:SS.MS');`, + Expected: []sql.Row{ + {"2021-09-15 21:43:56.123"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'HH HH12 HH24 hh hh12 hh24 H h hH Hh');`, + Expected: []sql.Row{ + {"09 09 21 09 09 21 H h hH Hh"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'MI mi M m');`, + Expected: []sql.Row{ + {"43 43 M m"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'SS ss S s MS ms Ms mS US us Us uS');`, + Expected: []sql.Row{ + {"56 56 S s 123 123 Ms mS 123457 123457 Us uS"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'Y,YYY y,yyy YYYY yyyy YYY yyy YY yy Y y');`, + Expected: []sql.Row{ + {"2,021 2,021 2021 2021 021 021 21 21 1 1"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'MONTH Month month MON Mon mon MM mm Mm mM');`, + Expected: []sql.Row{ + {"SEPTEMBER September september SEP Sep sep 09 09 Mm mM"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'DAY Day day DDD ddd DY Dy dy DD dd D d');`, + Expected: []sql.Row{ + {"WEDNESDAY Wednesday wednesday 258 258 WED Wed wed 15 15 4 4"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'DAY Day day DDD ddd DY Dy dy DD dd D d');`, + Expected: []sql.Row{ + {"WEDNESDAY Wednesday wednesday 258 258 WED Wed wed 15 15 4 4"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'IW iw');`, + Expected: []sql.Row{ + {"37 37"}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'AM PM am pm A.M. P.M. a.m. p.m.');`, + Expected: []sql.Row{ + {"PM PM pm pm P.M. P.M. p.m. p.m."}, + }, + }, + { + Query: `SELECT to_char(timestamp '2021-09-15 21:43:56.123456789', 'Q q');`, + Expected: []sql.Row{ + {"3 3"}, + }, + }, + }, + }, + }) +} diff --git a/testing/go/insert_test.go b/testing/go/insert_test.go index 0023f4f234..950b628542 100755 --- a/testing/go/insert_test.go +++ b/testing/go/insert_test.go @@ -150,5 +150,23 @@ func TestInsert(t *testing.T) { }, }, }, + { + Name: "implicit default values", + SetUpScript: []string{ + "CREATE TABLE t (i INT DEFAULT 123, j INT default 456);", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "INSERT INTO t DEFAULT VALUES;", + SkipResultsCheck: true, + }, + { + Query: "SELECT * FROM t", + Expected: []sql.Row{ + {123, 456}, + }, + }, + }, + }, }) } diff --git a/testing/go/regression/tool/main.go b/testing/go/regression/tool/main.go index 6d1b855832..4429b188c9 100644 --- a/testing/go/regression/tool/main.go +++ b/testing/go/regression/tool/main.go @@ -89,6 +89,8 @@ func main() { sb.WriteString(fmt.Sprintf("| Failures | %.4f%% | %.4f%% |\n", (float64(fromFail)/float64(fromTotal))*100.0, (float64(toFail)/float64(toTotal))*100.0)) + totalRegressions := 0 + totalProgressions := 0 if len(trackersFrom) == len(trackersTo) { // Handle regressions (which we'll display first) foundAnyFailDiff := false @@ -105,25 +107,28 @@ func main() { } for _, trackerToItem := range trackersTo[trackerIdx].FailPartialItems { if _, ok := fromFailItems[trackerToItem.Query]; !ok { - if !foundAnyFailDiff { - foundAnyFailDiff = true - sb.WriteString("\n## ${\\color{red}Regressions}$\n") + if totalRegressions < 40 { + if !foundAnyFailDiff { + foundAnyFailDiff = true + sb.WriteString("\n## ${\\color{red}Regressions__&&&&&&}$\n") + } + if !foundFileDiff { + foundFileDiff = true + sb.WriteString(fmt.Sprintf("### %s\n", trackersFrom[trackerIdx].File)) + } + sb.WriteString(fmt.Sprintf("```\nQUERY: %s\n", trackerToItem.Query)) + if len(trackerToItem.ExpectedError) != 0 { + sb.WriteString(fmt.Sprintf("EXPECTED ERROR: %s\n", trackerToItem.ExpectedError)) + } + if len(trackerToItem.UnexpectedError) != 0 { + sb.WriteString(fmt.Sprintf("RECEIVED ERROR: %s\n", trackerToItem.UnexpectedError)) + } + for _, partial := range trackerToItem.PartialSuccess { + sb.WriteString(fmt.Sprintf("PARTIAL: %s\n", partial)) + } + sb.WriteString("```\n") } - if !foundFileDiff { - foundFileDiff = true - sb.WriteString(fmt.Sprintf("### %s\n", trackersFrom[trackerIdx].File)) - } - sb.WriteString(fmt.Sprintf("```\nQUERY: %s\n", trackerToItem.Query)) - if len(trackerToItem.ExpectedError) != 0 { - sb.WriteString(fmt.Sprintf("EXPECTED ERROR: %s\n", trackerToItem.ExpectedError)) - } - if len(trackerToItem.UnexpectedError) != 0 { - sb.WriteString(fmt.Sprintf("RECEIVED ERROR: %s\n", trackerToItem.UnexpectedError)) - } - for _, partial := range trackerToItem.PartialSuccess { - sb.WriteString(fmt.Sprintf("PARTIAL: %s\n", partial)) - } - sb.WriteString("```\n") + totalRegressions += 1 } } } @@ -142,21 +147,27 @@ func main() { } for _, trackerToItem := range trackersTo[trackerIdx].SuccessItems { if _, ok := fromSuccessItems[trackerToItem.Query]; !ok { - if !foundAnySuccessDiff { - foundAnySuccessDiff = true - sb.WriteString("\n## ${\\color{lightgreen}Progressions}$\n") - } - if !foundFileDiff { - foundFileDiff = true - sb.WriteString(fmt.Sprintf("### %s\n", trackersFrom[trackerIdx].File)) + if totalProgressions < 40 { + if !foundAnySuccessDiff { + foundAnySuccessDiff = true + sb.WriteString("\n## ${\\color{lightgreen}Progressions__&&&&&&}$\n") + } + if !foundFileDiff { + foundFileDiff = true + sb.WriteString(fmt.Sprintf("### %s\n", trackersFrom[trackerIdx].File)) + } + sb.WriteString(fmt.Sprintf("```\nQUERY: %s\n```\n", trackerToItem.Query)) } - sb.WriteString(fmt.Sprintf("```\nQUERY: %s\n```\n", trackerToItem.Query)) + totalProgressions += 1 } } } } sb.WriteString("[^1]: These are tests that we're marking as `Successful`, however they do not match the expected output in some way. This is due to small differences, such as different wording on the error messages, or the column names being incorrect while the data itself is correct.") - fmt.Println(sb.String()) + output := sb.String() + output = strings.ReplaceAll(output, "Regressions__&&&&&&", fmt.Sprintf("Regressions (%d)", totalRegressions)) + output = strings.ReplaceAll(output, "Progressions__&&&&&&", fmt.Sprintf("Progressions (%d)", totalProgressions)) + fmt.Println(output) } func updateResults() { diff --git a/testing/go/regression/tool/replay.go b/testing/go/regression/tool/replay.go index 55537ac09c..53ec6f9a87 100644 --- a/testing/go/regression/tool/replay.go +++ b/testing/go/regression/tool/replay.go @@ -454,7 +454,7 @@ ListenerLoop: } } for _, failQuery := range options.FailQueries { - if message.String == failQuery { + if strings.Contains(message.String, failQuery) { tracker.Failed++ tracker.AddFailure(ReplayTrackerItem{ Query: message.String, diff --git a/testing/go/regression/tool/run_test.go b/testing/go/regression/tool/run_test.go index 0f7eaade3b..696d3fd7a6 100644 --- a/testing/go/regression/tool/run_test.go +++ b/testing/go/regression/tool/run_test.go @@ -102,4 +102,5 @@ WHERE pg_class.oid=indexrelid AND indrelid=pg_class_2.oid AND pg_class_2.relname = 'clstr_tst' AND indisclustered;`, + `SELECT 1 FROM pg_catalog.pg_constraint WHERE conrelid = i.indrelid AND conindid = i.indexrelid`, } diff --git a/testing/go/subqueries_test.go b/testing/go/subqueries_test.go index 7be234e1c9..beb7a33fcd 100755 --- a/testing/go/subqueries_test.go +++ b/testing/go/subqueries_test.go @@ -161,3 +161,49 @@ ORDER BY 1;`, }, }) } + +func TestExistSubquery(t *testing.T) { + RunScripts(t, []ScriptTest{ + { + Name: "basic case", + SetUpScript: []string{ + `CREATE TABLE test (id INT PRIMARY KEY);`, + `INSERT INTO test VALUES (1), (3), (2);`, + }, + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT * FROM test WHERE EXISTS (SELECT 123);`, + Expected: []sql.Row{ + {1}, + {2}, + {3}, + }, + }, + { + Query: `SELECT * FROM test WHERE NOT EXISTS (SELECT 123);`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT 123 WHERE EXISTS (SELECT * FROM test);`, + Expected: []sql.Row{ + {123}, + }, + }, + { + Query: `SELECT 123 WHERE EXISTS (SELECT * FROM test WHERE id > 10);`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT 123 WHERE NOT EXISTS (SELECT * FROM test);`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT 123 WHERE NOT EXISTS (SELECT * FROM test WHERE id > 10);`, + Expected: []sql.Row{ + {123}, + }, + }, + }, + }, + }) +} diff --git a/testing/go/union_test.go b/testing/go/union_test.go new file mode 100644 index 0000000000..40af1a445b --- /dev/null +++ b/testing/go/union_test.go @@ -0,0 +1,128 @@ +// Copyright 2024 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package _go + +import ( + "testing" + + "github.com/dolthub/go-mysql-server/sql" +) + +func TestUnion(t *testing.T) { + RunScripts(t, []ScriptTest{ + { + Name: "union tests", + SetUpScript: []string{ + `CREATE TABLE t1 (i INT PRIMARY KEY);`, + `CREATE TABLE t2 (j INT PRIMARY KEY);`, + `INSERT INTO t1 VALUES (1), (2), (3);`, + `INSERT INTO t2 VALUES (2), (3), (4);`, + }, + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT * FROM t1 UNION SELECT * FROM t2;`, + Expected: []sql.Row{ + {1}, + {2}, + {3}, + {4}, + }, + }, + { + Query: `SELECT 123 UNION SELECT 456;`, + Expected: []sql.Row{ + {123}, + {456}, + }, + }, + { + Query: `SELECT * FROM (VALUES (123), (456)) a UNION SELECT * FROM (VALUES (456), (789)) b;`, + Expected: []sql.Row{ + {123}, + {456}, + {789}, + }, + }, + }, + }, + }) +} + +func TestIntersect(t *testing.T) { + RunScripts(t, []ScriptTest{ + { + Name: "intersect tests", + SetUpScript: []string{ + `CREATE TABLE t1 (i INT PRIMARY KEY);`, + `CREATE TABLE t2 (j INT PRIMARY KEY);`, + `INSERT INTO t1 VALUES (1), (2), (3);`, + `INSERT INTO t2 VALUES (2), (3), (4);`, + }, + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT * FROM t1 INTERSECT SELECT * FROM t2;`, + Expected: []sql.Row{ + {2}, + {3}, + }, + }, + { + Query: `SELECT 123 INTERSECT SELECT 456;`, + Expected: []sql.Row{}, + }, + { + Query: `SELECT * FROM (VALUES (123), (456)) a INTERSECT SELECT * FROM (VALUES (456), (789)) b;`, + Expected: []sql.Row{ + {456}, + }, + }, + }, + }, + }) +} + +func TestExcept(t *testing.T) { + RunScripts(t, []ScriptTest{ + { + Name: "except tests", + SetUpScript: []string{ + `CREATE TABLE t1 (i INT PRIMARY KEY);`, + `CREATE TABLE t2 (j INT PRIMARY KEY);`, + `INSERT INTO t1 VALUES (1), (2), (3);`, + `INSERT INTO t2 VALUES (2), (3), (4);`, + }, + Assertions: []ScriptTestAssertion{ + { + Query: `SELECT * FROM t1 EXCEPT SELECT * FROM t2;`, + Expected: []sql.Row{ + {1}, + }, + }, + { + Query: `SELECT 123 EXCEPT SELECT 456;`, + Expected: []sql.Row{ + {123}, + }, + }, + { + Query: `SELECT * FROM (VALUES (123), (456)) a EXCEPT SELECT * FROM (VALUES (456), (789)) b;`, + Expected: []sql.Row{ + {123}, + }, + }, + }, + }, + }) +}