Skip to content

Commit a272452

Browse files
authored
Add mixed affinity UPDATE, unify UUID behaviour (#95)
1 parent 7dd696c commit a272452

File tree

27 files changed

+2088
-1412
lines changed

27 files changed

+2088
-1412
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ OBJS = connection.o option.o deparse.o sqlite_query.o sqlite_fdw.o sqlite_data_n
1515
EXTENSION = sqlite_fdw
1616
DATA = sqlite_fdw--1.0.sql sqlite_fdw--1.0--1.1.sql
1717

18-
REGRESS = extra/sqlite_fdw_post extra/float4 extra/float8 extra/int4 extra/int8 extra/numeric extra/join extra/limit extra/aggregates extra/prepare extra/select_having extra/select extra/insert extra/update extra/timestamp extra/encodings extra/bool extra/uuid sqlite_fdw type aggregate selectfunc
18+
REGRESS = extra/sqlite_fdw_post extra/bitstring extra/bool extra/float4 extra/float8 extra/int4 extra/int8 extra/numeric extra/out_of_range extra/timestamp extra/uuid extra/join extra/limit extra/aggregates extra/prepare extra/select_having extra/select extra/insert extra/update extra/encodings sqlite_fdw type aggregate selectfunc
1919
REGRESS_OPTS = --encoding=utf8
2020

2121
SQLITE_LIB = sqlite3

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ Features
3636
- Support Bulk `INSERT` by using `batch_size` option
3737
- Support `INSERT`/`UPDATE` with generated column
3838
- Support `ON CONFLICT DO NOTHING`
39-
- Support mixed SQLite [data affinity](https://www.sqlite.org/datatype3.html) input and filtering (`SELECT`/`WHERE` usage) for such dataypes as
39+
- 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),
4242
- `bool`: `text`(1..5) and `int`.
43-
- Support mixed SQLite [data affinity](https://www.sqlite.org/datatype3.html) output (`INSERT`/`UPDATE`) for such dataypes as
43+
- Support mixed SQLite [data affinity](https://www.sqlite.org/datatype3.html) output (`INSERT`/`UPDATE`) for such data types as
4444
- `timestamp`: `text`(default) or `int`,
4545
- `uuid`: `text`(36) or `blob`(16)(default).
4646

@@ -55,7 +55,7 @@ Features
5555
- `mod()` is pushdowned. In PostgreSQL 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).
5656
- `upper`, `lower` and other character case functions are **not** pushed down because they does not work with UNICODE character in SQLite.
5757
- `WITH TIES` option is **not** pushed down.
58-
- Bit string `#` (XOR) operator is **not** pushed down becasuse there is no equal SQLite operator.
58+
- Bit string `#` (XOR) operator is **not** pushed down because there is no equal SQLite operator.
5959

6060
### Notes about pushdowning
6161

@@ -69,7 +69,7 @@ Features
6969
- For `numeric` data type, `sqlite_fdw` use `sqlite3_column_double` to get value, while SQLite shell uses `sqlite3_column_text` to get value. Those 2 APIs may return different numeric value. Therefore, for `numeric` data type, the value returned from `sqlite_fdw` may different from the value returned from SQLite shell.
7070
- `sqlite_fdw` can return implementation-dependent order for column if the column is not specified in `ORDER BY` clause.
7171
- When the column type is `varchar array`, if the string is shorter than the declared length, values of type character will be space-padded; values of type `character varying` will simply store the shorter string.
72-
- [String literals for `boolean`](https://www.postgresql.org/docs/current/datatype-boolean.html) (`t`, `f`, `y`, `n`, `yes`, `no`, `on`, `off` etc. case insensetive) can be readed and filtred but cannot writed, because SQLite documentation recommends only `int` affinity values (`0` or `1`) for boolean data and usually text boolean data belongs to legacy datasets.
72+
- [String literals for `boolean`](https://www.postgresql.org/docs/current/datatype-boolean.html) (`t`, `f`, `y`, `n`, `yes`, `no`, `on`, `off` etc. case insensitive) can be readed and filtred but cannot writed, because SQLite documentation recommends only `int` affinity values (`0` or `1`) for boolean data and usually text boolean data belongs to legacy datasets.
7373

7474
Also see [Limitations](#limitations)
7575

@@ -227,7 +227,7 @@ In OS `sqlite_fdw` works as executed code with permissions of user of PostgreSQL
227227
in SQLite (mixed affinity case). Updated and inserted values will have this affinity. Default preferred SQLite affinity for `timestamp` and `uuid` PostgreSQL data types is `text`.
228228

229229
- Use `INT` value for SQLite column (epoch Unix Time) to be treated/visualized as `timestamp` in PostgreSQL.
230-
- Use `BLOB` value for SQLite column to be treated/visualized as `uuid` in PostgreSQL 14+.
230+
- Use `BLOB` value for SQLite column to be treated/visualized as `uuid`.
231231

232232
- **key** as *boolean*, optional, default *false*
233233

@@ -300,7 +300,7 @@ sqlite_fdw_version
300300
Identifier case handling
301301
------------------------
302302

303-
PostgreSQL folds identifiers to lower case by default, SQLite is case insensetive by default
303+
PostgreSQL folds identifiers to lower case by default, SQLite is case insensitive by default
304304
and doesn't differ uppercase and lowercase ASCII base latin letters. It's important
305305
to be aware of potential issues with table and column names.
306306

@@ -533,7 +533,7 @@ Limitations
533533
- 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.
534534
- 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.
535535
- 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.
536-
- 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 sensetive literals **only** in follow forms: `NaN`, `-Infinity`, `Infinity`, not original strings from `WHERE` condition. *This can caused selecting issues*.
536+
- 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*.
537537

538538
### Boolean values
539539
- `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`.
@@ -542,17 +542,17 @@ Limitations
542542
### UUID values
543543
- `sqlite_fdw` UUID values support exists only for `uuid` columns in foreign table. SQLite documentation recommends to store UUID as value with both `blob` and `text` [affinity](https://www.sqlite.org/datatype3.html). `sqlite_fdw` can pushdown both reading and filtering both `text` and `blob` values.
544544
- Expected affinity of UUID value in SQLite table determined by `column_type` option of the column
545-
for `INSERT` and `UPDATE` commands. In PostgreSQL 14- only `text` data affinity is availlable, PostgreSQL 14+ supports also `blob` data affinity.
545+
for `INSERT` and `UPDATE` commands. PostgreSQL supports both `blob` and `text` [affinity](https://www.sqlite.org/datatype3.html).
546546

547547
### bit and varbit support
548548
- `sqlite_fdw` PostgreSQL `bit`/`varbit` values support based on `int` SQLite data affinity, because there is no per bit operations for SQLite `blob` affinity data. Maximum SQLite `int` affinity value is 8 bytes length, hence maximum `bit`/`varbit` values length is 64 bits.
549-
- `sqlite_fdw` doesn't pushdown `#` (XOR) operator becasuse there is no equal SQLite operator.
549+
- `sqlite_fdw` doesn't pushdown `#` (XOR) operator because there is no equal SQLite operator.
550550

551551
Tests
552552
-----
553553
Test directory have structure as following:
554554

555-
```sql
555+
```
556556
+---sql
557557
| +---12.16
558558
| | filename1.sql

deparse.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ static void sqlite_get_relation_column_alias_ids(Var *node, RelOptInfo *foreignr
164164
static char *sqlite_quote_identifier(const char *s, char q);
165165
static bool sqlite_contain_immutable_functions_walker(Node *node, void *context);
166166
static bool sqlite_is_valid_type(Oid type);
167+
static int preferred_sqlite_affinity (Oid relid, int varattno);
167168

168169
/*
169170
* Append remote name of specified foreign table to buf.
@@ -2107,6 +2108,9 @@ sqlite_deparse_column_ref(StringInfo buf, int varno, int varattno, PlannerInfo *
21072108
}
21082109
}
21092110

2111+
/*
2112+
* Get column option with optionname for a variable attribute in deparsing context
2113+
*/
21102114
static char *
21112115
sqlite_deparse_column_option(int varno, int varattno, PlannerInfo *root, char *optionname)
21122116
{
@@ -2296,6 +2300,36 @@ sqlite_deparse_update(StringInfo buf, PlannerInfo *root,
22962300
}
22972301
}
22982302

2303+
/*
2304+
* Preferred SQLite affinity from "column_type" foreign column option
2305+
* SQLITE_NULL if no value or no normal value
2306+
*/
2307+
int
2308+
preferred_sqlite_affinity (Oid relid, int varattno)
2309+
{
2310+
char *coltype = NULL;
2311+
List *options;
2312+
ListCell *lc;
2313+
2314+
elog(DEBUG4, "sqlite_fdw : %s ", __func__);
2315+
if (varattno == 0)
2316+
return SQLITE_NULL;
2317+
2318+
options = GetForeignColumnOptions(relid, varattno);
2319+
foreach(lc, options)
2320+
{
2321+
DefElem *def = (DefElem *) lfirst(lc);
2322+
2323+
if (strcmp(def->defname, "column_type") == 0)
2324+
{
2325+
coltype = defGetString(def);
2326+
elog(DEBUG4, "column type = %s", coltype);
2327+
break;
2328+
}
2329+
}
2330+
return sqlite_affinity_code(coltype);
2331+
}
2332+
22992333
/*
23002334
* deparse remote UPDATE statement
23012335
*
@@ -2346,7 +2380,11 @@ sqlite_deparse_direct_update_sql(StringInfo buf, PlannerInfo *root,
23462380
forboth(lc, targetlist, lc2, targetAttrs)
23472381
{
23482382
int attnum = lfirst_int(lc2);
2383+
int preferred_affinity = SQLITE_NULL;
23492384
TargetEntry *tle;
2385+
RangeTblEntry *rte;
2386+
bool special_affinity = false;
2387+
Oid pg_attyp;
23502388
#if (PG_VERSION_NUM >= 140000)
23512389
tle = lfirst_node(TargetEntry, lc);
23522390

@@ -2366,8 +2404,25 @@ sqlite_deparse_direct_update_sql(StringInfo buf, PlannerInfo *root,
23662404
first = false;
23672405

23682406
sqlite_deparse_column_ref(buf, rtindex, attnum, root, false, true);
2407+
2408+
/* Get RangeTblEntry from array in PlannerInfo. */
2409+
rte = planner_rt_fetch(rtindex, root);
2410+
pg_attyp = get_atttype(rte->relid, attnum);
2411+
preferred_affinity = preferred_sqlite_affinity(rte->relid, attnum);
2412+
23692413
appendStringInfoString(buf, " = ");
2414+
if (pg_attyp == UUIDOID && preferred_affinity == SQLITE3_TEXT)
2415+
{
2416+
appendStringInfo(buf, "sqlite_fdw_uuid_str(");
2417+
special_affinity = true;
2418+
}
23702419
sqlite_deparse_expr((Expr *) tle->expr, &context);
2420+
2421+
if (special_affinity)
2422+
{
2423+
elog(DEBUG4, "sqlite_fdw : aff %d\n", preferred_affinity);
2424+
appendStringInfoString(buf, ")");
2425+
}
23712426
}
23722427

23732428
sqlite_reset_transmission_modes(nestlevel);

0 commit comments

Comments
 (0)