2019-03-24 09:57:29 -04:00
|
|
|
package psql
|
|
|
|
|
|
|
|
import (
|
2019-06-01 19:48:42 -04:00
|
|
|
"bytes"
|
2019-03-24 09:57:29 -04:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
2019-04-01 01:18:14 -04:00
|
|
|
func withComplexArgs(t *testing.T) {
|
2019-03-24 18:16:03 -04:00
|
|
|
gql := `query {
|
2019-06-14 22:17:21 -04:00
|
|
|
proDUcts(
|
2019-03-24 18:16:03 -04:00
|
|
|
# returns only 30 items
|
|
|
|
limit: 30,
|
2020-02-10 12:15:37 +05:30
|
|
|
|
2019-03-24 18:16:03 -04:00
|
|
|
# starts from item 10, commented out for now
|
|
|
|
# offset: 10,
|
2020-02-10 12:15:37 +05:30
|
|
|
|
2019-03-24 18:16:03 -04:00
|
|
|
# orders the response items by highest price
|
|
|
|
order_by: { price: desc },
|
2020-02-10 12:15:37 +05:30
|
|
|
|
2019-03-24 18:16:03 -04:00
|
|
|
# no duplicate prices returned
|
|
|
|
distinct: [ price ]
|
2020-02-10 12:15:37 +05:30
|
|
|
|
2019-10-03 03:08:01 -04:00
|
|
|
# only items with an id >= 20 and < 28 are returned
|
2019-03-24 18:16:03 -04:00
|
|
|
where: { id: { and: { greater_or_equals: 20, lt: 28 } } }) {
|
|
|
|
id
|
2019-06-14 22:17:21 -04:00
|
|
|
NAME
|
2019-03-24 18:16:03 -04:00
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-27 02:58:19 -04:00
|
|
|
}
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
func withWhereAndList(t *testing.T) {
|
2019-03-27 02:58:19 -04:00
|
|
|
gql := `query {
|
|
|
|
products(
|
2019-06-01 02:03:09 -04:00
|
|
|
where: {
|
2020-02-10 12:15:37 +05:30
|
|
|
and: [
|
|
|
|
{ not: { id: { is_null: true } } },
|
|
|
|
{ price: { gt: 10 } },
|
|
|
|
] } ) {
|
2019-03-27 02:58:19 -04:00
|
|
|
id
|
|
|
|
name
|
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-27 02:58:19 -04:00
|
|
|
}
|
|
|
|
|
2019-04-01 01:18:14 -04:00
|
|
|
func withWhereIsNull(t *testing.T) {
|
2019-03-27 02:58:19 -04:00
|
|
|
gql := `query {
|
|
|
|
products(
|
2019-06-01 02:03:09 -04:00
|
|
|
where: {
|
|
|
|
and: {
|
|
|
|
not: { id: { is_null: true } },
|
|
|
|
price: { gt: 10 }
|
2019-03-27 02:58:19 -04:00
|
|
|
}}) {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-27 02:58:19 -04:00
|
|
|
}
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
func withWhereMultiOr(t *testing.T) {
|
2019-03-27 02:58:19 -04:00
|
|
|
gql := `query {
|
|
|
|
products(
|
2019-06-01 02:03:09 -04:00
|
|
|
where: {
|
2020-02-10 12:15:37 +05:30
|
|
|
or: {
|
|
|
|
not: { id: { is_null: true } },
|
|
|
|
price: { gt: 10 },
|
|
|
|
price: { lt: 20 }
|
|
|
|
} }
|
|
|
|
) {
|
2019-03-27 02:58:19 -04:00
|
|
|
id
|
|
|
|
name
|
|
|
|
price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
|
2019-06-01 02:03:09 -04:00
|
|
|
func fetchByID(t *testing.T) {
|
2019-03-24 18:16:03 -04:00
|
|
|
gql := `query {
|
2019-06-01 02:03:09 -04:00
|
|
|
product(id: 15) {
|
|
|
|
id
|
|
|
|
name
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
|
2019-06-01 02:03:09 -04:00
|
|
|
func searchQuery(t *testing.T) {
|
2019-03-24 18:16:03 -04:00
|
|
|
gql := `query {
|
2019-12-02 10:52:22 -05:00
|
|
|
products(search: "ale") {
|
2019-06-01 02:03:09 -04:00
|
|
|
id
|
2019-03-24 18:16:03 -04:00
|
|
|
name
|
2019-12-02 10:52:22 -05:00
|
|
|
search_rank
|
|
|
|
search_headline_description
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "admin")
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
|
2019-06-01 02:03:09 -04:00
|
|
|
func oneToMany(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
users {
|
|
|
|
email
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
price
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
|
2019-12-09 01:48:18 -05:00
|
|
|
func oneToManyReverse(t *testing.T) {
|
2019-03-24 18:16:03 -04:00
|
|
|
gql := `query {
|
2019-06-01 02:03:09 -04:00
|
|
|
products {
|
|
|
|
name
|
|
|
|
price
|
|
|
|
users {
|
|
|
|
email
|
2019-03-24 18:16:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-03-24 09:57:29 -04:00
|
|
|
}
|
2019-03-24 23:42:54 -04:00
|
|
|
|
2019-12-09 01:48:18 -05:00
|
|
|
func oneToManyArray(t *testing.T) {
|
2020-01-16 01:44:19 -05:00
|
|
|
gql := `
|
|
|
|
query {
|
|
|
|
product {
|
|
|
|
name
|
|
|
|
price
|
|
|
|
tags {
|
|
|
|
id
|
2019-12-09 01:48:18 -05:00
|
|
|
name
|
|
|
|
}
|
2020-01-16 01:44:19 -05:00
|
|
|
}
|
|
|
|
tags {
|
|
|
|
name
|
|
|
|
product {
|
2019-12-09 01:48:18 -05:00
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "admin")
|
2019-12-09 01:48:18 -05:00
|
|
|
}
|
|
|
|
|
2019-06-01 02:03:09 -04:00
|
|
|
func manyToMany(t *testing.T) {
|
2019-04-04 00:53:24 -04:00
|
|
|
gql := `query {
|
2019-06-01 02:03:09 -04:00
|
|
|
products {
|
2019-04-04 00:53:24 -04:00
|
|
|
name
|
2019-06-01 02:03:09 -04:00
|
|
|
customers {
|
|
|
|
email
|
|
|
|
full_name
|
|
|
|
}
|
2019-04-04 00:53:24 -04:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-04 00:53:24 -04:00
|
|
|
}
|
|
|
|
|
2019-06-01 02:03:09 -04:00
|
|
|
func manyToManyReverse(t *testing.T) {
|
2019-04-05 01:44:30 -04:00
|
|
|
gql := `query {
|
2019-06-01 02:03:09 -04:00
|
|
|
customers {
|
|
|
|
email
|
|
|
|
full_name
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
}
|
2019-04-05 01:44:30 -04:00
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-05 01:44:30 -04:00
|
|
|
}
|
|
|
|
|
2019-04-01 01:18:14 -04:00
|
|
|
func aggFunction(t *testing.T) {
|
2019-03-31 11:18:33 -04:00
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
count_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-10-14 02:51:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func aggFunctionBlockedByCol(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
count_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "anon")
|
2019-10-14 02:51:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func aggFunctionDisabled(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
name
|
|
|
|
count_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "anon1")
|
2019-03-31 11:18:33 -04:00
|
|
|
}
|
|
|
|
|
2019-04-01 01:18:14 -04:00
|
|
|
func aggFunctionWithFilter(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products(where: { id: { gt: 10 } }) {
|
|
|
|
id
|
|
|
|
max_price
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-08 21:24:29 -04:00
|
|
|
}
|
|
|
|
|
2019-11-04 23:44:42 -05:00
|
|
|
func syntheticTables(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
me {
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-11-04 23:44:42 -05:00
|
|
|
}
|
|
|
|
|
2019-04-20 00:35:57 -04:00
|
|
|
func queryWithVariables(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
product(id: $PRODUCT_ID, where: { price: { eq: $PRODUCT_PRICE } }) {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-20 00:35:57 -04:00
|
|
|
}
|
|
|
|
|
2019-11-04 23:44:42 -05:00
|
|
|
func withWhereOnRelations(t *testing.T) {
|
2019-04-08 21:24:29 -04:00
|
|
|
gql := `query {
|
2019-11-04 23:44:42 -05:00
|
|
|
users(where: {
|
|
|
|
not: {
|
|
|
|
products: {
|
2020-01-13 09:34:15 -05:00
|
|
|
price: { gt: 3 }
|
2019-11-04 23:44:42 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}) {
|
|
|
|
id
|
2019-04-08 21:24:29 -04:00
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-11-19 00:47:55 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func multiRoot(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
product {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
customer {
|
|
|
|
email
|
|
|
|
}
|
|
|
|
customers {
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}
|
|
|
|
user {
|
|
|
|
id
|
|
|
|
email
|
|
|
|
}
|
|
|
|
customer {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "user")
|
2019-04-01 01:18:14 -04:00
|
|
|
}
|
|
|
|
|
2020-01-28 00:26:53 -05:00
|
|
|
func jsonColumnAsTable(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
tag_count {
|
|
|
|
count
|
|
|
|
tags {
|
|
|
|
name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "admin")
|
2020-01-28 00:26:53 -05:00
|
|
|
}
|
|
|
|
|
2020-01-20 23:38:17 -05:00
|
|
|
func skipUserIDForAnonRole(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
products {
|
|
|
|
id
|
|
|
|
name
|
|
|
|
user(where: { id: { eq: $user_id } }) {
|
|
|
|
id
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "anon")
|
2020-01-20 23:38:17 -05:00
|
|
|
}
|
|
|
|
|
2019-11-04 23:44:42 -05:00
|
|
|
func blockedQuery(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
user(id: 5, where: { id: { gt: 3 } }) {
|
|
|
|
id
|
|
|
|
full_name
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "bad_dude")
|
2019-11-04 23:44:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func blockedFunctions(t *testing.T) {
|
|
|
|
gql := `query {
|
|
|
|
users {
|
|
|
|
count_id
|
|
|
|
email
|
|
|
|
}
|
|
|
|
}`
|
|
|
|
|
2020-02-10 12:15:37 +05:30
|
|
|
compileGQLToPSQL(t, gql, nil, "bad_dude")
|
2019-11-04 23:44:42 -05:00
|
|
|
}
|
|
|
|
|
2019-10-14 02:51:36 -04:00
|
|
|
func TestCompileQuery(t *testing.T) {
|
2019-04-01 01:18:14 -04:00
|
|
|
t.Run("withComplexArgs", withComplexArgs)
|
|
|
|
t.Run("withWhereAndList", withWhereAndList)
|
|
|
|
t.Run("withWhereIsNull", withWhereIsNull)
|
|
|
|
t.Run("withWhereMultiOr", withWhereMultiOr)
|
2019-04-04 00:53:24 -04:00
|
|
|
t.Run("fetchByID", fetchByID)
|
2019-04-05 01:44:30 -04:00
|
|
|
t.Run("searchQuery", searchQuery)
|
2019-04-01 01:18:14 -04:00
|
|
|
t.Run("oneToMany", oneToMany)
|
2019-12-09 01:48:18 -05:00
|
|
|
t.Run("oneToManyReverse", oneToManyReverse)
|
|
|
|
t.Run("oneToManyArray", oneToManyArray)
|
2019-04-01 01:18:14 -04:00
|
|
|
t.Run("manyToMany", manyToMany)
|
|
|
|
t.Run("manyToManyReverse", manyToManyReverse)
|
|
|
|
t.Run("aggFunction", aggFunction)
|
2019-10-14 02:51:36 -04:00
|
|
|
t.Run("aggFunctionBlockedByCol", aggFunctionBlockedByCol)
|
|
|
|
t.Run("aggFunctionDisabled", aggFunctionDisabled)
|
2019-04-01 01:18:14 -04:00
|
|
|
t.Run("aggFunctionWithFilter", aggFunctionWithFilter)
|
2019-04-08 21:24:29 -04:00
|
|
|
t.Run("syntheticTables", syntheticTables)
|
2019-10-03 03:08:01 -04:00
|
|
|
t.Run("queryWithVariables", queryWithVariables)
|
2019-11-04 23:44:42 -05:00
|
|
|
t.Run("withWhereOnRelations", withWhereOnRelations)
|
2019-11-19 00:47:55 -05:00
|
|
|
t.Run("multiRoot", multiRoot)
|
2020-01-28 00:26:53 -05:00
|
|
|
t.Run("jsonColumnAsTable", jsonColumnAsTable)
|
2020-01-20 23:38:17 -05:00
|
|
|
t.Run("skipUserIDForAnonRole", skipUserIDForAnonRole)
|
2019-10-26 01:34:29 -04:00
|
|
|
t.Run("blockedQuery", blockedQuery)
|
2019-10-27 01:52:48 -04:00
|
|
|
t.Run("blockedFunctions", blockedFunctions)
|
2019-04-01 01:18:14 -04:00
|
|
|
}
|
|
|
|
|
2019-06-14 22:17:21 -04:00
|
|
|
var benchGQL = []byte(`query {
|
|
|
|
proDUcts(
|
2019-06-02 01:38:51 -04:00
|
|
|
# returns only 30 items
|
|
|
|
limit: 30,
|
|
|
|
|
|
|
|
# starts from item 10, commented out for now
|
|
|
|
# offset: 10,
|
|
|
|
|
|
|
|
# orders the response items by highest price
|
|
|
|
order_by: { price: desc },
|
|
|
|
|
|
|
|
# only items with an id >= 30 and < 30 are returned
|
|
|
|
where: { id: { and: { greater_or_equals: 20, lt: 28 } } }) {
|
|
|
|
id
|
2019-06-14 22:17:21 -04:00
|
|
|
NAME
|
2019-06-02 01:38:51 -04:00
|
|
|
price
|
|
|
|
user {
|
|
|
|
full_name
|
|
|
|
picture : avatar
|
2019-03-24 23:42:54 -04:00
|
|
|
}
|
2019-06-02 01:38:51 -04:00
|
|
|
}
|
2019-06-14 22:17:21 -04:00
|
|
|
}`)
|
2019-03-24 23:42:54 -04:00
|
|
|
|
2019-06-02 01:38:51 -04:00
|
|
|
func BenchmarkCompile(b *testing.B) {
|
|
|
|
w := &bytes.Buffer{}
|
2019-06-01 19:48:42 -04:00
|
|
|
|
2019-03-24 23:42:54 -04:00
|
|
|
b.ResetTimer()
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
|
|
|
for n := 0; n < b.N; n++ {
|
2019-06-02 01:38:51 -04:00
|
|
|
w.Reset()
|
|
|
|
|
2019-10-14 02:51:36 -04:00
|
|
|
qc, err := qcompile.Compile(benchGQL, "user")
|
2019-06-01 19:48:42 -04:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2019-09-05 00:09:56 -04:00
|
|
|
_, err = pcompile.Compile(qc, w, nil)
|
2019-03-24 23:42:54 -04:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-02 01:38:51 -04:00
|
|
|
|
|
|
|
func BenchmarkCompileParallel(b *testing.B) {
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
|
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
|
|
w := &bytes.Buffer{}
|
|
|
|
|
|
|
|
for pb.Next() {
|
|
|
|
w.Reset()
|
|
|
|
|
2019-10-14 02:51:36 -04:00
|
|
|
qc, err := qcompile.Compile(benchGQL, "user")
|
2019-06-02 01:38:51 -04:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2019-09-05 00:09:56 -04:00
|
|
|
_, err = pcompile.Compile(qc, w, nil)
|
2019-06-02 01:38:51 -04:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|