From 673ed830ef1e652053b34d7152a1b59048cfbc3a Mon Sep 17 00:00:00 2001 From: dbs Date: Mon, 26 Oct 2009 14:30:26 +0000 Subject: [PATCH] Kill smart quotes and emdashes beloved by MS Word and despised by the rest of the world This enables openjade-based DocBook processing toolchains to actually transform the document. git-svn-id: svn://svn.open-ils.org/ILS/trunk@14603 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- docs/TechRef/JSONTutorial.xml | 262 +++++++++++++++++----------------- 1 file changed, 131 insertions(+), 131 deletions(-) diff --git a/docs/TechRef/JSONTutorial.xml b/docs/TechRef/JSONTutorial.xml index afc0cf3554..068a13a604 100644 --- a/docs/TechRef/JSONTutorial.xml +++ b/docs/TechRef/JSONTutorial.xml @@ -41,7 +41,7 @@ doesn't have to. Nevertheless, the need to encode a query in a JSON string adds complications, because the client needs to know how to build the right JSON. JSON queries are also - somewhat limiting – they can't do all of the things that you can do with raw SQL. + somewhat limiting -- they can't do all of the things that you can do with raw SQL. This tutorial explains what you can do with a JSON query, and how you can do it. @@ -63,7 +63,7 @@ Definitions - References to “SQL” refer to the dialect implemented by PostgreSQL. This tutorial + References to "SQL" refer to the dialect implemented by PostgreSQL. This tutorial assumes that you are already familiar with SQL. You should also be familiar with JSON. However it is worth defining a couple of terms that have other meanings in other contexts: @@ -71,22 +71,22 @@ - An “object” is a JSON object, i.e. a comma-separated list of name:value pairs, + An "object" is a JSON object, i.e. a comma-separated list of name:value pairs, enclosed in curly braces, like this: - { “a”:”frobozz”, “b”:24, “c”:null } + { "a":"frobozz", "b":24, "c":null } - An “array” is a JSON array, i.e. a comma-separated list of values, enclosed + An "array" is a JSON array, i.e. a comma-separated list of values, enclosed in square brackets, like this: - [ “Goober”, 629, null, false, “glub” ] + [ "Goober", 629, null, false, "glub" ] @@ -104,7 +104,7 @@ few related tables. The queries themselves are designed to illustrate the syntax, not to do anything useful at the application level. For example, it's not meaningful to take the square root of an org_unit id, except to illustrate how to code a function call. - The examples are like department store mannequins – they have no brains, they're only + The examples are like department store mannequins -- they have no brains, they're only for display. @@ -156,8 +156,8 @@ the default SELECT clause. If the FROM clause joins two or more tables, the default SELECT clause includes columns only from the core table, not from any of the joined tables. - The default SELECT clause has almost the same effect as “SELECT *”, - but not exactly. If you were to “SELECT * from actor.org_unit_type + The default SELECT clause has almost the same effect as "SELECT *", + but not exactly. If you were to "SELECT * from actor.org_unit_type in psql, the output would include all the same columns as in the example above, but not in the same order. A default SELECT clause includes the columns in the order in which the IDL defines them, which may be different from the order in which the database defines them. @@ -171,7 +171,7 @@ There are other ways to get a default SELECT clause. However, default SELECT clauses are a distraction at this point, because most of the time you'll specify your own SELECT clause explicitly, as we will discuss later. - Let's consider some more important aspects of this simple example – more important + Let's consider some more important aspects of this simple example -- more important because they apply to more complex queries as well. @@ -236,7 +236,7 @@ While this syntax may not be terribly useful, it does illustrate the minimal structure of a SELECT clause in a JSON query: an entry in the outermost JSON object, with a key of - “select”. The value associated with this key is another JSON object, + "select". The value associated with this key is another JSON object, whose keys are class names. (These two examples also illustrate another point: unlike SQL, a JSON query doesn't care whether the FROM clause or the SELECT clause comes first.) @@ -279,34 +279,34 @@ - “column” -- the column name (required). + "column" -- the column name (required). - “alias” -- used to define a column alias, which + "alias" -- used to define a column alias, which otherwise defaults to the column name. - “aggregate” -- takes a value of + "aggregate" -- takes a value of true or false. Don't worry about this one yet. It concerns the use of GROUP BY clauses, which we will examine later. - “transform” -- the name of an SQL function to be + "transform" -- the name of an SQL function to be called. - “result_field” -- used with - “transform”; specifies an output column of a function that + "result_field" -- used with + "transform"; specifies an output column of a function that returns multiple columns at a time. - “params” -- used with “transform”; + "params" -- used with "transform"; provides a list of parameters for the function. They may be strings, numbers, or nulls. @@ -340,7 +340,7 @@ In this case, changing the column alias doesn't accomplish much. But if we - were joining to the actor.org_unit_type table, which also has a “name” column, + were joining to the actor.org_unit_type table, which also has a "name" column, we could use different aliases to distinguish them. The following example uses a function to raise a column to upper case: @@ -368,7 +368,7 @@ - Here we take a substring of the name, using the “params” + Here we take a substring of the name, using the "params" element to pass parameters: @@ -398,14 +398,14 @@ - The parameters specified with “params” are inserted - after the applicable column (“name” in this + The parameters specified with "params" are inserted + after the applicable column ("name" in this case), which is always the first parameter. They are always passed as strings, i.e. enclosed in quotes, even if the JSON expresses them as numbers. PostgreSQL will ordinarily coerce them to the right type. However if the function name is overloaded to accept different types, PostgreSQL may invoke a function other than the one intended. - Finally we call a fictitious function “frobozz” that returns + Finally we call a fictitious function "frobozz" that returns multiple columns, where we want only one of them: @@ -435,7 +435,7 @@ - The “frobozz” function doesn't actually exist, but json_query doesn't know + The "frobozz" function doesn't actually exist, but json_query doesn't know that. The query won't fail until json_query tries to execute it in the database. @@ -449,7 +449,7 @@ This limitation doesn't matter. The results are returned in the form of a data structure, which the client program can navigate however it likes. You can't select an arbitrary expression, such as - “percentage / 100” or “last_name || ', ' || first_name”. + "percentage / 100" or "last_name || ', ' || first_name". Most of the time this limitation doesn't matter either, because the client program can do these kinds of manipulations for itself. However, function calls may be a problem. You can't nest them, and you can't pass more than one column value to them (and it has @@ -497,7 +497,7 @@ Like the SELECT clause, the WHERE clause gets its own entry in the top-level object - of a JSON query. The key is “where”, and the associated value is + of a JSON query. The key is "where", and the associated value is either an object (as shown here) or an array (to be discussed a bit later). Each entry in the object is a separate condition. In this case, we use a special shortcut for expressing an equality condition. The @@ -519,9 +519,9 @@ Like the SELECT clause, the generated WHERE clause qualifies each column name with the alias of the relevant table. - If you want to compare a column to NULL, put “null” (without + If you want to compare a column to NULL, put "null" (without quotation marks) to the right of the colon instead of a literal value. The resulting - SQL will include “IS NULL” instead of an equals sign. + SQL will include "IS NULL" instead of an equals sign. Other Kinds of Comparisons @@ -573,8 +573,8 @@ - The condition '“=”:null' turns into IS NULL. Any other - operator used with “null” turns into IS NOT NULL. + The condition '"=":null' turns into IS NULL. Any other + operator used with "null" turns into IS NOT NULL. You can use most of the comparison operators recognized by PostgreSQL: @@ -585,8 +585,8 @@ similar to - The only ones you can't use are “is distinct from” and - “is not distinct from”. + The only ones you can't use are "is distinct from" and + "is not distinct from". @@ -594,7 +594,7 @@ Here's a dirty little secret: json_query doesn't really pay much attention to the operator you supply. It merely checks to make sure that the operator doesn't contain any semicolons or white space, in order to prevent certain kinds of SQL injection. - It also allows “similar to” as a special exception. + It also allows "similar to" as a special exception. As a result, you can slip an operator of your own devising into the SQL, so long as it doesn't contain any semicolons or white space, and doesn't create invalid syntax. Here's a contrived and rather silly example: @@ -690,7 +690,7 @@ - In a JSON query this approach doesn't work. If you try it, the “= true” test will + In a JSON query this approach doesn't work. If you try it, the "= true" test will turn into IS NULL. Don't do that. Instead, use a leading plus sign, as described in the preceding section, to treat the boolean column as a stand-alone condition: @@ -720,7 +720,7 @@ If you need to test for falsity, then write a test for truth and negate it with the - “-not” operator. We will discuss the “-not” operator later, but + "-not" operator. We will discuss the "-not" operator later, but here's a preview: @@ -781,13 +781,13 @@ In this case we compare the boolean column to a single simple condition. However you - can include additional complications – multiple conditions, IN lists, BETWEEN clauses, + can include additional complications -- multiple conditions, IN lists, BETWEEN clauses, and other features as described below. Multiple Conditions - If you need multiple conditions, just add them to the “where” + If you need multiple conditions, just add them to the "where" object, separated by commas: @@ -844,9 +844,9 @@ - “where”: { - “parent_ou”:{ “>”:3 }, - “parent_ou”:{ “<>”:7 } + "where": { + "parent_ou":{ ">":3 }, + "parent_ou":{ "<>":7 } } @@ -857,17 +857,17 @@ - “where”: { - “parent_ou”: { - “>”:3, - “<>”:7 + "where": { + "parent_ou": { + ">":3, + "<>":7 } } - Nice try, but that doesn't work either. Maybe it ought to work – at least it's - legal JSON – but, no. + Nice try, but that doesn't work either. Maybe it ought to work -- at least it's + legal JSON -- but, no. Here's what works: @@ -956,7 +956,7 @@ - We use “-or” as the key, with the conditions to be ORed in an + We use "-or" as the key, with the conditions to be ORed in an associated object. The leading minus sign is there to make sure that the operator isn't confused with a column name. Later we'll see some other operators with leading minus signs. In a couple of spots we even use plus signs. @@ -977,7 +977,7 @@ - The conditions paired with “-or” are linked by OR and enclosed + The conditions paired with "-or" are linked by OR and enclosed in parentheses, Here's how to do the same thing using an array, except that it produces an extra layer of parentheses: @@ -1013,20 +1013,20 @@ It's possible, though not very useful, to have only a single condition subject to - the “-or” operator. In that case, the condition appears by itself, + the "-or" operator. In that case, the condition appears by itself, since there's nothing to OR it to. This trick is another way to add an extraneous layer of parentheses, Another way to AND - You can also use the “-and” operator. It works just like - “-or”, except that it combines conditions with AND instead of OR. + You can also use the "-and" operator. It works just like + "-or", except that it combines conditions with AND instead of OR. Since AND is the default, we don't usually need a separate operator for it, but it's available. - In rare cases, nothing else will do – you can't include two conditions in the same + In rare cases, nothing else will do -- you can't include two conditions in the same list because of the duplicate key problem, but you can't combine them with arrays either. In particular, you might need to combine them within an expression that you're comparing to a boolean column (see the subsection above on Testing Boolean @@ -1035,7 +1035,7 @@ Negation with NOT - The “-not” operator negates a condition or set of conditions. + The "-not" operator negates a condition or set of conditions. For example: @@ -1071,17 +1071,17 @@ In this example we merely negate a combination of two comparisons. However the condition to be negated may be as complicated as it needs to be. Anything that can - be subject to “where” can be subject to - “-not”. + be subject to "where" can be subject to + "-not". In most cases you can achieve the same result by other means. However the - “-not” operator is the only way to represent NOT BETWEEN + "-not" operator is the only way to represent NOT BETWEEN (to be discussed later). EXISTS with Subqueries - Two other operators carry a leading minus sign: “-exists” - and its negation “-not-exists”. These operators apply to + Two other operators carry a leading minus sign: "-exists" + and its negation "-not-exists". These operators apply to subqueries, which have the same format as a full query. For example: @@ -1143,7 +1143,7 @@ - Note the use of “+aou” to qualify the id column in the + Note the use of "+aou" to qualify the id column in the inner WHERE clause. @@ -1185,7 +1185,7 @@ The value associated with the column name is an object with a single - entry, whose key is “between”. The corresponding + entry, whose key is "between". The corresponding value is an array with exactly two values, defining the range to be tested. The range bounds must be either numbers or string literals. Although @@ -1257,16 +1257,16 @@ This version results in the same SQL as the first one. For a NOT IN list, you can use the latter format, using the - “not in” operator instead of “in”. + "not in" operator instead of "in". Alternatively, you can use either format together with the - “-not” operator. + "-not" operator. IN and NOT IN Clauses with Subqueries For an IN clause with a subquery, the syntax is similar to the second of the two formats for an IN list (see the previous subsection). The - “in” or “not in” operator is paired, + "in" or "not in" operator is paired, not with an array of values, but with an object representing the subquery. For example: @@ -1312,8 +1312,8 @@ In SQL the subquery may select multiple columns, but in a JSON query it can select only a single column. - For a NOT IN clause with a subquery, use the “not in” - operator instead of “in”. + For a NOT IN clause with a subquery, use the "not in" + operator instead of "in". @@ -1332,7 +1332,7 @@ - A comparison operator (“>” in this case) is paired + A comparison operator (">" in this case) is paired with an array. The first entry in the array must be a string giving the name of the function. The remaining parameters, if any, are the parameters. They may be strings, numbers, or nulls. The resulting SQL for this example: @@ -1380,8 +1380,8 @@ - The “transform” entry gives the name of the function that we - will use on the left side of the comparison. The “value” entry + The "transform" entry gives the name of the function that we + will use on the left side of the comparison. The "value" entry designates the value on the right side of the comparison. @@ -1398,7 +1398,7 @@ As in the SELECT clause, you can pass literal values or nulls to the function as additional parameters by using an array tagged as - “params”: + "params": @@ -1434,9 +1434,9 @@ followed by any additional parameters (which are always enclosed in quotes even if they are numeric). As in the SELECT clause: if the function returns multiple columns, you can specify - the one you want by using a “result_field” entry (not shown + the one you want by using a "result_field" entry (not shown here). - If you leave out the “transform” entry (or misspell it), the + If you leave out the "transform" entry (or misspell it), the column name will appear on the left without any function call. This syntax works, but it's more complicated than it needs to be. @@ -1444,7 +1444,7 @@ Putting Function Calls on Both Sides If you want to compare one function call to another, you can use the same syntax - shown in the previous subsection – except that the “value” entry + shown in the previous subsection -- except that the "value" entry carries an array instead of a literal value. For example: @@ -1484,10 +1484,10 @@ For a function call to the left of the comparison, the function name is - tagged as “transform”. The first parameter is always the + tagged as "transform". The first parameter is always the relevant column name; additional parameters, if any, are in an array tagged - as “params”. The entry for - “result_field”, if present, specifies a subcolumn. + as "params". The entry for + "result_field", if present, specifies a subcolumn. @@ -1502,7 +1502,7 @@ Comparing a Function to a Condition - So far we have seen two kinds of data for the “value” tag. A + So far we have seen two kinds of data for the "value" tag. A string or number translates to a literal value, and an array translates to a function call. The third possibility is a JSON object, which translates to a condition. For example: @@ -1524,9 +1524,9 @@ - The function tagged as “transform” must return boolean, or else + The function tagged as "transform" must return boolean, or else json_query will generate invalid SQL. The function used here, - “is_prime”, is fictitious. + "is_prime", is fictitious. @@ -1542,7 +1542,7 @@ - If we left out the “transform” entry, json_query would compare + If we left out the "transform" entry, json_query would compare the column on the left (which would to be boolean) to the condition on the right. The results are similar to those for a simpler format described earlier (see the subsection Testing Boolean Columns). @@ -1557,7 +1557,7 @@ However, in the WHERE clause these limitations are more limiting, because the client program can't compensate by doing some of the work for itself. You can't use arbitrary expressions in a WHERE condition, such as - “WHERE id > parent_ou – 3”. In some cases you may be able to + "WHERE id > parent_ou -- 3". In some cases you may be able to contrive a custom operator in order to fake such an expression. However this mechanism is neither very general nor very aesthetic. To the right of a comparison operator, all function parameters must be literals or @@ -1567,7 +1567,7 @@ You can't include null values in an IN list or a BETWEEN list, not that you should ever want to. As noted earlier: you can't use the comparison operators - “is distinct from” or “is not distinct from”. + "is distinct from" or "is not distinct from". Also as noted earlier: a subquery in an IN clause cannot select more than one column. @@ -1577,7 +1577,7 @@ JOIN clauses Until now, our examples have selected from only one table at a time. As a result, - the FROM clause has been very simple – just a single string containing the class name of + the FROM clause has been very simple -- just a single string containing the class name of the relevant table. When the FROM clause joins multiple tables, the corresponding JSON naturally gets more complicated. @@ -1626,7 +1626,7 @@ First, let's review the SELECT clause. Since it selects rows from two different tables, - the data for “select” includes two entries, one for each table. + the data for "select" includes two entries, one for each table. As for the FROM clause, it's no longer just a string. It's a JSON object, with exactly one entry. The key of this entry is the class name of the core table, i.e. the table named immediately after the FROM keyword. The data associated with this key contains the @@ -1708,18 +1708,18 @@ the attributes of the join. Later we'll encounter other kinds of join attributes. For now, the only attributes that we're looking at are the ones that identify the join columns: - “fkey” and “field”. The hard part is + "fkey" and "field". The hard part is remembering which is which: - “fkey” identifies the join column from the + "fkey" identifies the join column from the left table; - “field” identifies the join column from the + "field" identifies the join column from the right table. @@ -1781,7 +1781,7 @@ Specifying Only One Join Column We just saw how to specify both ends of a join. It turns out that there's a - shortcut – most of the time you only need to specify one end. Consider the + shortcut -- most of the time you only need to specify one end. Consider the following variation on the previous example: @@ -1886,8 +1886,8 @@ - The “join” attribute introduces another level of join. In this - case “aou” is the left table for the nested join, and the right table + The "join" attribute introduces another level of join. In this + case "aou" is the left table for the nested join, and the right table for the original join. Here are the results: @@ -1929,9 +1929,9 @@ Here we asked for a left outer join. For a right outer join, code - “type”:”right”. For a full outer join, code - “type”:”full”. Any other value for “type” results in an inner - join, so watch out for typos. A type of “rihgt” will give you + "type":"right". For a full outer join, code + "type":"full". Any other value for "type" results in an inner + join, so watch out for typos. A type of "rihgt" will give you a wrong join instead of a right one. Here is the resulting SQL for this example: @@ -1971,7 +1971,7 @@ - Note the peculiar operator “+aou” -- a plus sign followed + Note the peculiar operator "+aou" -- a plus sign followed by the relevant class name. This operator tells json_query to apply the specified class to the condition that follows. The result: @@ -2102,9 +2102,9 @@ - By default, json_query uses AND to combine the “filter” + By default, json_query uses AND to combine the "filter" condition with the original join condition. If you need OR, you can use the - “filter_op” attribute to say so: + "filter_op" attribute to say so: @@ -2137,10 +2137,10 @@ - If the data tagged by “filter_op” is anything but - “or” (in upper, lower, or mixed case), json_query uses AND + If the data tagged by "filter_op" is anything but + "or" (in upper, lower, or mixed case), json_query uses AND instead of OR. - The condition tagged by “filter” may be much more complicated. + The condition tagged by "filter" may be much more complicated. In fact it accepts all the same syntax as the WHERE clause. Remember, though, that it all gets combined with the the original join condition with an AND, or with an OR if you so specify. If you're not careful, the result @@ -2187,7 +2187,7 @@ - The “iatc” class is like a view, except that it's defined in the + The "iatc" class is like a view, except that it's defined in the IDL instead of the database. In this case it provides a way to do a join that would otherwise be impossible through a JSON query, because it joins the same table in two different ways (see the next subsection). @@ -2283,7 +2283,7 @@ - The data associated with “from” is an array instead of a string + The data associated with "from" is an array instead of a string or an object. The first element in the array specifies the name of the function. Subsequent elements, if any, supply the parameters of the function; they must be literal values or nulls. @@ -2360,12 +2360,12 @@ - The “class” tag provides the name of the class, + The "class" tag provides the name of the class, which must be either the core class or a joined class. - The “field” tag provides the field name, corresponding + The "field" tag provides the field name, corresponding to one of the columns of the class. @@ -2374,7 +2374,7 @@ If you want to sort by multiple fields, just include a separate object for each field. If you want to sort a field in descending order, add a - “direction” tag: + "direction" tag: @@ -2399,14 +2399,14 @@ - The string tagged as “direction” can be anything – all that - matters is the first character. If the string starts with “D” or “d”, the sort + The string tagged as "direction" can be anything -- all that + matters is the first character. If the string starts with "D" or "d", the sort will be descending. Otherwise it will be ascending. So - “diplodocus” or “Dioscorides” will work as - well as “desc”, but “going down” means that + "diplodocus" or "Dioscorides" will work as + well as "desc", but "going down" means that the sort will go up. You can also pass a column through some kind of transforming function, much as - you can in the SELECT and WHERE clauses, using the “transform” + you can in the SELECT and WHERE clauses, using the "transform" tag. For example, for a case-insensitive sort, you could raise to upper case: @@ -2437,7 +2437,7 @@ If you need additional parameters for the function, you can use the - “params” tag to pass them: + "params" tag to pass them: @@ -2473,7 +2473,7 @@ As we have seen elsewhere, all literal values are passed as quoted strings, even if they are numbers. If the function returns multiple columns, you can use the - “result_field” tag to indicate which one you want (not shown). + "result_field" tag to indicate which one you want (not shown). @@ -2496,17 +2496,17 @@ - For the “aout” class, the associated array is simply a list + For the "aout" class, the associated array is simply a list of field names (in this case, just one). Naturally, each field must reside in the class with which it is associated. However, a list of field names provides no way to specify the direction of sorting, or a transforming function. You can add those details only if the class name is paired with an object, as in the example for the - “aou” class. The keys for such an object are field names, and + "aou" class. The keys for such an object are field names, and the associated tags define other details. - In this example, we use the “direction” tag to specify that + In this example, we use the "direction" tag to specify that the name field be sorted in descending order. This tag works the same way here as - described earlier. If the associated string starts with “D” or “d”, the sort will + described earlier. If the associated string starts with "D" or "d", the sort will be descending; otherwise it will be ascending. Here is the resulting SQL: @@ -2524,8 +2524,8 @@ - You can also use the “transform”, “params”, - and “result_field” tags to specify the use of a transforming + You can also use the "transform", "params", + and "result_field" tags to specify the use of a transforming function, as described in the previous subsection. For example: @@ -2570,8 +2570,8 @@ there are situations where it can be useful, provided that the column is passed to a transforming function. For example, you might want a case-insensitive sort, except that for any given - letter you want lower case to sort first. For example, you want “diBona” to sort - before “Dibona”. Here's a way to do that, coding the ORDER BY clause as an array: + letter you want lower case to sort first. For example, you want "diBona" to sort + before "Dibona". Here's a way to do that, coding the ORDER BY clause as an array: @@ -2624,8 +2624,8 @@ - The “transform” tag is there just to give us an excuse to do a GROUP - BY. What's important to notice is the “aggregate” tag. + The "transform" tag is there just to give us an excuse to do a GROUP + BY. What's important to notice is the "aggregate" tag. Here's the resulting SQL: @@ -2642,17 +2642,17 @@ The GROUP BY clause references fields from the SELECT clause by numerical reference, instead of by repeating them. Notice that the field it references, parent_ou, is the - one that doesn't carry the “aggregate” tag in + one that doesn't carry the "aggregate" tag in the JSON. Let's state that more generally. The GROUP BY clause includes only the fields that - do not carry the “aggregate” tag (or that carry + do not carry the "aggregate" tag (or that carry it with a value of false). However, that logic applies only when some field somewhere does - carry the “aggregate” tag, with a value of true. If there is no - “aggregate” tag, or it appears only with a value of false, then there + carry the "aggregate" tag, with a value of true. If there is no + "aggregate" tag, or it appears only with a value of false, then there is no GROUP BY clause. If you really want to include every field in the GROUP BY clause, don't use - “aggregate”. Use the “distinct” tag, as described + "aggregate". Use the "distinct" tag, as described in the next section. @@ -2678,8 +2678,8 @@ - Note the “distinct” entry at the top level of the - query object, with a value of “true”. + Note the "distinct" entry at the top level of the + query object, with a value of "true". @@ -2699,7 +2699,7 @@ The HAVING Clause - For a HAVING clause, add a “having” entry at the top level + For a HAVING clause, add a "having" entry at the top level of the query object. For the associated data, you can use all the same syntax that you can use for a WHERE clause. Here's a simple example: @@ -2730,7 +2730,7 @@ - We use the “aggregate” tag in the SELECT clause to give us a GROUP BY to go + We use the "aggregate" tag in the SELECT clause to give us a GROUP BY to go with the HAVING. Results: @@ -2747,7 +2747,7 @@ - In raw SQL we could have referred to “count( 1 )”. But since JSON queries + In raw SQL we could have referred to "count( 1 )". But since JSON queries cannot encode arbitrary expressions, we applied the count function to a column that cannot be null. @@ -2771,7 +2771,7 @@ - The data associated with “offset” and “limit” + The data associated with "offset" and "limit" may be either a number or a string, but if it's a string, it should have a number inside. Result: -- 2.43.2