Skip to content

Commit c81bc84

Browse files
authored
Full ∞ support for numeric context (#97)
1 parent cdcf0ba commit c81bc84

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+10992
-405
lines changed

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,20 @@ Features
3939
- Support mixed SQLite [data affinity](https://www.sqlite.org/datatype3.html) input and filtering (`SELECT`/`WHERE` usage) for such data types as
4040
- `timestamp`: `text` and `int`,
4141
- `uuid`: `text`(32..39) and `blob`(16),
42-
- `bool`: `text`(1..5) and `int`.
42+
- `bool`: `text`(1..5) and `int`,
43+
- `double precision`, `float` and `numeric`: `real` values and special values with `text` affinity (`+Infinity` and `-Infinity`).
4344
- Support mixed SQLite [data affinity](https://www.sqlite.org/datatype3.html) output (`INSERT`/`UPDATE`) for such data types as
4445
- `timestamp`: `text`(default) or `int`,
4546
- `uuid`: `text`(36) or `blob`(16)(default).
47+
- Full support for `+Infinity` (means ∞) and `-Infinity` (means -∞) special values for IEEE 754-2008 numbers in `double precision`, `float` and `numeric` columns including such conditions as ` n < '+Infinity'` or ` m > '-Infinity'`.
4648

4749
### Pushdowning
4850
- `WHERE` clauses are pushdowned
4951
- Aggregate function are pushdowned
5052
- `ORDER BY` is pushdowned
5153
- Joins (left/right/inner/cross) are pushdowned
5254
- `CASE` expressions are pushdowned.
53-
- `LIMIT` and `OFFSET` are pushdowned (*when all tables queried are fdw)
55+
- `LIMIT` and `OFFSET` are pushdowned when all tables in the query are foreign tables belongs to the same PostgreSQL `FOREIGN SERVER` object.
5456
- Support `GROUP BY`, `HAVING` push-down.
5557
- `mod()` is pushdowned. In PostgreSQL this function gives [argument-dependend data type](https://www.postgresql.org/docs/current/functions-math.html), but result from SQLite always [have `real` affinity](https://www.sqlite.org/lang_mathfunc.html#mod).
5658
- `upper`, `lower` and other character case functions are **not** pushed down because they does not work with UNICODE character in SQLite.
@@ -129,7 +131,7 @@ Usage
129131

130132
- **database** as *string*, **required**, no default
131133

132-
SQLite database path.
134+
SQLite database file address.
133135

134136
- **updatable** as *boolean*, optional, default *true*
135137

@@ -557,6 +559,9 @@ Limitations
557559
- `TRUNCATE` in `sqlite_fdw` always delete data of both parent and child tables (no matter user inputs `TRUNCATE table CASCADE` or `TRUNCATE table RESTRICT`) if there are foreign-keys references with `ON DELETE CASCADE` clause.
558560
- `RETURNING` is not supported.
559561

562+
### Mixed affinity support
563+
SQLite `text` affinity values which is different for SQLite unique checks can be equal for PostgreSQL because `sqlite_fdw` unifyes semantics of values, not storage form. For example `1`(integer), `Y`(text) and `tRuE`(text) SQLite values is different in SQLite but equal in PostgreSQL as `true` values of `boolean` column. This is also applicable for a data with `text` affinity in `uuid`, `timestamp`, `double precision`, `float` and `numeric` columns of foreign tables. **Please be carefully if you want to use mixed affinity column as PostgreSQL foreign table primary key**.
564+
560565
### Arrays
561566
- `sqlite_fdw` only supports `ARRAY` const, for example, `ANY (ARRAY[1, 2, 3])` or `ANY ('{1, 2 ,3}')`.
562567
- `sqlite_fdw` does not support `ARRAY` expression, for example, `ANY (ARRAY[c1, 1, c1+0])`.
@@ -566,7 +571,8 @@ Limitations
566571
- For `sum` function of SQLite, output of `sum(bigint)` is `integer` value. If input values are big, the overflow error may occurs on SQLite because it overflow within the range of signed 64bit. For PostgreSQL, it can calculate as over the precision of `bigint`, so overflow does not occur.
567572
- SQLite promises to preserve the 15 most significant digits of a floating point value. The big value which exceed 15 most significant digits may become different value after inserted.
568573
- SQLite does not support `numeric` type as PostgreSQL. Therefore, it does not allow to store numbers with too high precision and scale. Error out of range occurs.
569-
- SQLite does not support special values for IEEE 754-2008 numbers such as `NaN`, `+Infinity` and `-Infinity` in SQL expressions with numeric context. Also SQLite can not store this values with `real` [affinity](https://www.sqlite.org/datatype3.html). In opposite to SQLite, PostgreSQL can store special values in columns belongs to `real` datatype family such as `float` or `double precision` and use arithmetic comparation for this values. In oppose to PostgreSQL, SQLite stores `NaN`, `+Infinity` and `-Infinity` as a text values. Also conditions with special literals (such as ` n < '+Infinity'` or ` m > '-Infinity'` ) isn't numeric conditions in SQLite and gives unexpected result after pushdowning in oppose to internal PostgreSQL calculations. During `INSERT INTO ... SELECT` or in `WHERE` conditions `sqlite_fdw` uses given by PostgreSQL standard case sensitive literals **only** in follow forms: `NaN`, `-Infinity`, `Infinity`, not original strings from `WHERE` condition. *This can caused selecting issues*.
574+
- SQLite does not support `NaN` special value for IEEE 754-2008 numbers. Please use this special value very cerefully because there is no such conception in SQLite at all and `NaN` value treated in SQLite as `NULL`.
575+
- SQLite support `+Infinity` and `-Infinity` special values for IEEE 754-2008 numbers in SQL expressions with numeric context. This values can be readed with both `text` and `real` affiniy, but can be writed to SQLite only with `real` affinity (as signed out of range value `9e999`).
570576

571577
### Boolean values
572578
- `sqlite_fdw` boolean values support exists only for `bool` columns in foreign table. SQLite documentation recommends to store boolean as value with `integer` [affinity](https://www.sqlite.org/datatype3.html). `NULL` isn't converted, 1 converted to `true`, all other `NOT NULL` values converted to `false`. During `SELECT ... WHERE condition_column` condition converted only to `condition_column`.

deparse.c

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* Portions Copyright (c) 2018, TOSHIBA CORPORATION
66
*
77
* IDENTIFICATION
8-
* deparse.c
8+
* deparse.c
99
*
1010
*-------------------------------------------------------------------------
1111
*/
@@ -2080,7 +2080,16 @@ sqlite_deparse_column_ref(StringInfo buf, int varno, int varattno, PlannerInfo *
20802080
* Recommended form for normalisation is someone from 1<->1 with PostgreSQL
20812081
* internal storage, hence usually this will not original text data.
20822082
*/
2083-
if (!dml_context && pg_atttyp == BOOLOID)
2083+
if (!dml_context && ( pg_atttyp == FLOAT8OID || pg_atttyp == FLOAT4OID || pg_atttyp == NUMERICOID) )
2084+
{
2085+
elog(DEBUG2, "floatN unification for \"%s\"", colname);
2086+
appendStringInfoString(buf, "sqlite_fdw_float(");
2087+
if (qualify_col)
2088+
ADD_REL_QUALIFIER(buf, varno);
2089+
appendStringInfoString(buf, sqlite_quote_identifier(colname, '`'));
2090+
appendStringInfoString(buf, ")");
2091+
}
2092+
else if (!dml_context && pg_atttyp == BOOLOID)
20842093
{
20852094
elog(DEBUG2, "boolean unification for \"%s\"", colname);
20862095
appendStringInfoString(buf, "sqlite_fdw_bool(");
@@ -2648,12 +2657,26 @@ sqlite_deparse_const(Const *node, deparse_expr_cxt *context, int showtype)
26482657
case INT4OID:
26492658
case INT8OID:
26502659
case OIDOID:
2660+
{
2661+
extval = OidOutputFunctionCall(typoutput, node->constvalue);
2662+
if (strspn(extval, "0123456789+-eE.") == strlen(extval))
2663+
{
2664+
if (extval[0] == '+' || extval[0] == '-')
2665+
appendStringInfo(buf, "(%s)", extval);
2666+
else
2667+
appendStringInfoString(buf, extval);
2668+
}
2669+
else
2670+
ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
2671+
errmsg("Invalid input syntax. Invalid characters in number"),
2672+
errhint("Value: %s", extval)));
2673+
}
2674+
break;
26512675
case FLOAT4OID:
26522676
case FLOAT8OID:
26532677
case NUMERICOID:
26542678
{
26552679
extval = OidOutputFunctionCall(typoutput, node->constvalue);
2656-
26572680
/*
26582681
* No need to quote unless it's a special value such as 'NaN' or 'Infinity'.
26592682
* See comments in get_const_expr().
@@ -2665,8 +2688,28 @@ sqlite_deparse_const(Const *node, deparse_expr_cxt *context, int showtype)
26652688
else
26662689
appendStringInfoString(buf, extval);
26672690
}
2691+
else if (strcasecmp(extval, "Inf") == 0 ||
2692+
strcasecmp(extval, "Infinity") == 0 ||
2693+
strcasecmp(extval + 1, "Inf") == 0 ||
2694+
strcasecmp(extval + 1, "Infinity") == 0)
2695+
{
2696+
bool is_negative_or_positive = false;
2697+
if (extval[0] == '-' || extval[0] == '+')
2698+
is_negative_or_positive = true;
2699+
2700+
if (is_negative_or_positive)
2701+
appendStringInfo(buf, "(%c", extval[0]);
2702+
2703+
appendStringInfo(buf, "9e999");
2704+
2705+
if (is_negative_or_positive)
2706+
appendStringInfo(buf, ")");
2707+
}
26682708
else
2709+
{
2710+
/* NaN and other */
26692711
appendStringInfo(buf, "\'%s\'", extval);
2712+
}
26702713
}
26712714
break;
26722715
case BITOID:
@@ -2677,7 +2720,7 @@ sqlite_deparse_const(Const *node, deparse_expr_cxt *context, int showtype)
26772720
{
26782721
ereport(ERROR, (errcode(ERRCODE_FDW_INVALID_DATA_TYPE),
26792722
errmsg("SQLite FDW dosens't support very long bit/varbit data"),
2680-
errhint("bit length %ld, maxmum %ld", strlen(extval), SQLITE_FDW_BIT_DATATYPE_BUF_SIZE - 1)));
2723+
errhint("bit length %ld, maximum %ld", strlen(extval), SQLITE_FDW_BIT_DATATYPE_BUF_SIZE - 1)));
26812724
}
26822725
appendStringInfo(buf, "%lld", binstr2int64(extval));
26832726
}
@@ -2726,12 +2769,8 @@ sqlite_deparse_const(Const *node, deparse_expr_cxt *context, int showtype)
27262769
}
27272770
break;
27282771
case UUIDOID:
2729-
/* always deparse to BLOB because this is internal PostgreSQL storage
2730-
* the string for BYTEA always seems to be in the format "\\x##"
2731-
* where # is a hex digit, Even if the value passed in is
2732-
* 'hi'::bytea we will receive "\x6869". Making this assumption
2733-
* allows us to quickly convert postgres escaped strings to sqlite
2734-
* ones for comparison
2772+
/* always deparse to BLOB, in case of UPDATE with text affinity
2773+
* transformation function will be added
27352774
*/
27362775
{
27372776
int i = 0;
@@ -3031,7 +3070,13 @@ sqlite_deparse_scalar_array_op_expr(ScalarArrayOpExpr *node, deparse_expr_cxt *c
30313070
switch (c->consttype)
30323071
{
30333072
case INT4ARRAYOID:
3073+
case INT8ARRAYOID:
3074+
case INT2ARRAYOID:
3075+
case BOOLARRAYOID:
30343076
case OIDARRAYOID:
3077+
case NUMERICARRAYOID:
3078+
case FLOAT4ARRAYOID:
3079+
case FLOAT8ARRAYOID:
30353080
isstr = false;
30363081
break;
30373082
default:

0 commit comments

Comments
 (0)