Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bool mixed affinity support #88

Merged
merged 30 commits into from
Dec 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
aab2fa2
Add UUID tests Pg 11-15, refactor mixed affinity file, add pushdowning.
mkgrgis Oct 30, 2023
5e3f149
Add `bool` mixed affinity support, refactor tests
mkgrgis Nov 8, 2023
a8e0664
Add function about unwanted void text
mkgrgis Nov 9, 2023
2848a96
Fix code style and warnings
mkgrgis Nov 9, 2023
bd4296d
Update license info
mkgrgis Nov 9, 2023
4a03c82
Fixme datatype support table
mkgrgis Nov 9, 2023
2558505
Add copyright list
mkgrgis Nov 9, 2023
207fcce
Refactor and expand bool tests
mkgrgis Nov 10, 2023
a2a3148
Fix bool test dependences
mkgrgis Nov 10, 2023
806cf4c
Merge branch 'master' into bool_mixed_affinity_support
mkgrgis Nov 16, 2023
63d4907
Fix tests after merging with master
mkgrgis Nov 16, 2023
2417c4b
Update documentation about mixed affinity
mkgrgis Nov 16, 2023
f7172eb
Fix typo error
mkgrgis Nov 16, 2023
f002985
Fix tests: add type change
mkgrgis Nov 21, 2023
b78b5ed
Revert README.md
mkgrgis Nov 29, 2023
2a2261e
Add operator tests, `UPDATE` and `DELETE` tests.
mkgrgis Nov 29, 2023
78af8f8
Merge branch 'bool_mixed_affinity_support' of ssh://github.com/mkgrgi…
mkgrgis Nov 29, 2023
6ff3790
Revert License
mkgrgis Nov 30, 2023
6ccdf19
Reduce operation tests to unique combinations
mkgrgis Nov 30, 2023
e81840a
Revert date in license, README.md
mkgrgis Nov 30, 2023
08d8748
Improve gcc warning fix
mkgrgis Dec 1, 2023
71a1276
Remove encoding test artifacts from other test
mkgrgis Dec 1, 2023
72e1b22
Fix SQLite data unifying functions and rc processing
mkgrgis Dec 15, 2023
3f61bd8
Fix indents, sqlite_data_norm.c
mkgrgis Dec 15, 2023
15a428e
Delete trailing whitespaces (by linter)
mkgrgis Dec 15, 2023
78406b9
RELEASE_v2.4.0-test (#93)
MinhLA1410 Dec 27, 2023
2bd1510
`error_helper` as static, sqlite_data_norm.c
mkgrgis Dec 27, 2023
2bdcc1b
Remove connection closing, sqlite_data_norm.c
mkgrgis Dec 27, 2023
899191c
Merge branch 'bool_mixed_affinity_support'
mkgrgis Dec 27, 2023
44b7f21
Delete sql/15.3/sqlite_fdw.sql
mkgrgis Dec 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
##########################################################################

MODULE_big = sqlite_fdw
OBJS = connection.o option.o deparse.o sqlite_query.o sqlite_fdw.o uuid_extension.o
OBJS = connection.o option.o deparse.o sqlite_query.o sqlite_fdw.o sqlite_data_norm.o

EXTENSION = sqlite_fdw
DATA = sqlite_fdw--1.0.sql sqlite_fdw--1.0--1.1.sql

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 sqlite_fdw type aggregate selectfunc
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
REGRESS_OPTS = --encoding=utf8

SQLITE_LIB = sqlite3
Expand Down
78 changes: 49 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,14 @@ Features
- Support discard cached connections to foreign servers by using function `sqlite_fdw_disconnect()`, `sqlite_fdw_disconnect_all()`.
- Support Bulk `INSERT` by using `batch_size` option
- Support `INSERT`/`UPDATE` with generated column
- Support `ON CONFLICT DO NOTHING`.
- Support `ON CONFLICT DO NOTHING`
- Support mixed SQLite [data affinity](https://www.sqlite.org/datatype3.html) input and filtering (`SELECT`/`WHERE` usage) for such dataypes as
- `timestamp`: `text` and `int`,
- `uuid`: `text`(32..39) and `blob`(16),
- `bool`: `text`(1..5) and `int`.
- Support mixed SQLite [data affinity](https://www.sqlite.org/datatype3.html) output (`INSERT`/`UPDATE`) for such dataypes as
- `timestamp`: `text`(default) or `int`,
- `uuid`: `text`(36) or `blob`(16)(default).
mkgrgis marked this conversation as resolved.
Show resolved Hide resolved

### Pushdowning
- `WHERE` clauses are pushdowned
Expand All @@ -62,6 +69,7 @@ Features
- 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.
- `sqlite_fdw` can return implementation-dependent order for column if the column is not specified in `ORDER BY` clause.
- 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.
- [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.

Also see [Limitations](#limitations)

Expand Down Expand Up @@ -122,7 +130,6 @@ This table represents `sqlite_fdw` behaviour if in PostgreSQL foreign table colu

* **∅** - no support (runtime error)
* **V** - transparent transformation
* **b** - show per-bit form
* **T** - cast to text in SQLite utf-8 encoding, then to **PostgreSQL text with current encoding of database** and then transparent transformation if applicable
* **✔** - transparent transformation where PostgreSQL datatype is equal to SQLite affinity
* **V+** - transparent transformation if appliacable
Expand All @@ -133,14 +140,14 @@ SQLite `NULL` affinity always can be transparent converted for a nullable column

| PostgreSQL | SQLite <br> INT | SQLite <br> REAL | SQLite <br> BLOB | SQLite <br> TEXT | SQLite <br> TEXT but <br>empty|SQLite<br>nearest<br>affinity|
|-------------:|:------------:|:------------:|:------------:|:------------:|:------------:|-------------:|
| bool | V | ? | T | - | ∅ | INT |
| bit(n) | V n<=64 | ∅ | V | ? | ∅ | INT |
| bytea | b | b | ✔ | - | ? | BLOB |
| bool | V | | T | V+ | ∅ | INT |
| bit(n) | V n<=64 | ∅ | | | ∅ | INT |
| bytea | | | ✔ | - | ? | BLOB |
| date | V | V | T | V+ | `NULL` | ? |
| float4 | V+ | ✔ | T | - | `NULL` | REAL |
| float8 | V+ | ✔ | T | - | `NULL` | REAL |
| int2 | | ? | T | - | `NULL` | INT |
| int4 | | ? | T | - | `NULL` | INT |
| int2 | V+ | ? | T | - | `NULL` | INT |
| int4 | V+ | ? | T | - | `NULL` | INT |
| int8 | ✔ | ? | T | - | `NULL` | INT |
| json | ? | ? | T | V+ | ? | TEXT |
| name | ? | ? | T | V | `NULL` | TEXT |
Expand All @@ -151,7 +158,7 @@ SQLite `NULL` affinity always can be transparent converted for a nullable column
|timestamp + tz| V | V | T | V+ | `NULL` | ? |
| uuid | ∅ | ∅ |V+<br>(only<br>16 bytes)| V+ | ∅ | TEXT, BLOB |
| varchar | ? | ? | T | ✔ | V | TEXT |
| varbit(n) | V n<=64 | ∅ | V | ? | ∅ | INT |
| varbit(n) | V n<=64 | ∅ | V | | ∅ | INT |

### CREATE SERVER options

Expand All @@ -160,19 +167,19 @@ SQLite `NULL` affinity always can be transparent converted for a nullable column
- **database** as *string*, **required**, no default

SQLite database path.

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

This option allow or disallow write operations on SQLite database file.

- **truncatable** as *boolean*, optional, default *true*

Allows foreign tables to be truncated using the `TRUNCATE` command.

- **keep_connections** as *boolean*, optional, default *true*

Allows to keep connections to SQLite while there is no SQL operations between PostgreSQL and SQLite.

- **batch_size** as *integer*, optional, default *1*

Specifies the number of rows which should be inserted in a single `INSERT` operation. This setting can be overridden for individual tables.
Expand All @@ -196,17 +203,17 @@ In OS `sqlite_fdw` works as executed code with permissions of user of PostgreSQL
SQLite table name. Use if not equal to name of foreign table in PostgreSQL. Also see about [identifier case handling](#identifier-case-handling).

- **truncatable** as *boolean*, optional, default from the same `CREATE SERVER` option

See `CREATE SERVER` options section for details.

- **batch_size** as *integer*, optional, default from the same `CREATE SERVER` option

See `CREATE SERVER` options section for details.

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

This option can allow or disallow write operations on a SQLite table independed of the same server option.

`sqlite_fdw` accepts the following column-level options via the
`CREATE FOREIGN TABLE` command:

Expand All @@ -216,10 +223,11 @@ In OS `sqlite_fdw` works as executed code with permissions of user of PostgreSQL

- **column_type** as *string*, optional, no default

Gives preferred SQLite affinity for some PostgreSQL data types can be stored in different ways in SQLite. Default preferred SQLite affinity for this types is `text`.
Set preferred SQLite affinity for some PostgreSQL data types can be stored in different ways
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`.

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

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

Expand Down Expand Up @@ -305,7 +313,7 @@ Following SQL isn't correct for SQLite: `Error: duplicate column name: a`, but i
);
```
Following SQLs is correct for both SQLite and PostgreSQL because there is no column
names with ASCII base latin letters *only*.
with names composed from ASCII base latin letters *only*.

```sql
CREATE TABLE T_кир (
Expand All @@ -331,6 +339,19 @@ For SQLite there is no difference between
SELECT * FROM "T"; -- №4
```
For PostgreSQL the query with comment `№4` is independend query to table `T`, not to table `t` as other queries.
Please note this table name composed from ASCII base latin letters *only*. This is not applicable for other
alphabet systems or mixed names. This is because `toLower` operation in PostgreSQL is Unicode opration but
ASCII only operation in SQLite, hence other characters will not be changed.

```sql
SELECT * FROM т; -- №5
SELECT * FROM Т; -- №6
SELECT * FROM "т"; -- №7
SELECT * FROM "Т"; -- №8
```
In this case for PostgreSQL the query with comment `№8` is independend query to table `Т`, not to table `т`
as other queries. But for SQLite the queries with comments `№6` and `№8` belongs to table `Т`, and the queries with
comments `№5` and `№7` belongs to table `т`.

If there is

Expand Down Expand Up @@ -521,7 +542,7 @@ Limitations
### UUID values
- `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.
- Expected affinity of UUID value in SQLite table determined by `column_type` option of the column
for `INSERT` and `UPDATE` commands.
for `INSERT` and `UPDATE` commands. In PostgreSQL 14- only `text` data affinity is availlable, PostgreSQL 14+ supports also `blob` data affinity.

### bit and varbit support
- `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.
Expand All @@ -533,30 +554,30 @@ Test directory have structure as following:

```sql
+---sql
| +---12.15
| +---12.16
| | filename1.sql
| | filename2.sql
| |
| +---13.11
| +---13.12
| | filename1.sql
| | filename2.sql
| |
.................
| \---15.3
| \---15.4
| filename1.sql
| filename2.sql
|
\---expected
| +---12.15
| +---12.16
| | filename1.out
| | filename2.out
| |
| +---13.11
| +---13.12
| | filename1.out
| | filename2.out
| |
.................
| \---15.3
| \---15.4
filename1.out
filename2.out
```
Expand Down Expand Up @@ -615,9 +636,8 @@ Useful links

License
-------

Copyright (c) 2018, TOSHIBA CORPORATION
Copyright (c) 2011 - 2016, EnterpriseDB Corporation
* Copyright © 2018, TOSHIBA CORPORATION
* Copyright © 2011 - 2016, EnterpriseDB Corporation

Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies.

Expand Down
12 changes: 1 addition & 11 deletions connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,17 +220,7 @@ sqlite_open_db(const char *dbpath)
/* add included inner SQLite functions from separate c file
* for using in data unifying during deparsing
*/
rc = sqlite_fdw_data_norm_functs_init(conn);
if (rc != SQLITE_OK)
{
char *perr = pstrdup(err);

sqlite3_free(err);
sqlite3_close(conn);
ereport(ERROR,
(errcode(ERRCODE_FDW_UNABLE_TO_ESTABLISH_CONNECTION),
errmsg("failed to create UUID support function for SQLite DB. rc=%d err=%s", rc, perr)));
}
sqlite_fdw_data_norm_functs_init(conn);
return conn;
}

Expand Down
33 changes: 18 additions & 15 deletions deparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ sqlite_foreign_expr_walker(Node *node,
|| strcmp(opername, "round") == 0
|| strcmp(opername, "rtrim") == 0
|| strcmp(opername, "substr") == 0
|| strcmp(opername, "mod") == 0))
|| strcmp(opername, "mod") == 0 ))
{
return false;
}
Expand All @@ -622,7 +622,6 @@ sqlite_foreign_expr_walker(Node *node,
glob_cxt, &inner_cxt, case_arg_cxt))
return false;


/*
* If function's input collation is not derived from a foreign
* Var, it can't be sent to remote.
Expand Down Expand Up @@ -675,7 +674,7 @@ sqlite_foreign_expr_walker(Node *node,
ReleaseSysCache(tuple);

/*
* Factorial (!) and Bitwise XOR (^), (#)
* Factorial (!) and Bitwise XOR (^), (#)
* cannot be pushed down to SQLite
* Full list see in https://www.postgresql.org/docs/current/functions-bitstring.html
* ILIKE cannot be pushed down to SQLite
Expand Down Expand Up @@ -2082,29 +2081,33 @@ sqlite_deparse_column_ref(StringInfo buf, int varno, int varattno, PlannerInfo *
colname = get_attname(rte->relid, varattno);
#endif
pg_atttyp = get_atttype(rte->relid, varattno);

/* PostgreSQL data types with possible mixed affinity SQLite base we should
* normalize to preferred form in SQLite before transfer to PostgreSQL.
* Recommended form for normalisation is someone from 1<->1 with PostgreSQL
* internal storage, hence usually this will not original text data.
*/
if (pg_atttyp == UUIDOID && !dml_context )
if (!dml_context && pg_atttyp == BOOLOID)
{
elog(DEBUG2, "UUID unification for \"%s\"", colname);
/* Please remove to UNHEX and deattach uuid_extension.c after SQLite 3.41+ support */
appendStringInfoString(buf, "coalesce(sqlite_fdw_uuid_blob(");
elog(DEBUG2, "boolean unification for \"%s\"", colname);
appendStringInfoString(buf, "sqlite_fdw_bool(");
if (qualify_col)
ADD_REL_QUALIFIER(buf, varno);
appendStringInfoString(buf, sqlite_quote_identifier(colname, '`'));
appendStringInfoString(buf, "),");
appendStringInfoString(buf, ")");
}
else if (!dml_context && pg_atttyp == UUIDOID)
{
elog(DEBUG2, "UUID unification for \"%s\"", colname);
appendStringInfoString(buf, "sqlite_fdw_uuid_blob(");
if (qualify_col)
ADD_REL_QUALIFIER(buf, varno);
appendStringInfoString(buf, sqlite_quote_identifier(colname, '`'));
appendStringInfoString(buf, ")");
}
else
else
{
elog(DEBUG3, "column name without data unification = \"%s\"", colname);
elog(DEBUG4, "column name without data unification = \"%s\"", colname);
if (qualify_col)
ADD_REL_QUALIFIER(buf, varno);
appendStringInfoString(buf, sqlite_quote_identifier(colname, '`'));
Expand Down Expand Up @@ -2443,7 +2446,7 @@ sqlite_deparse_direct_delete_sql(StringInfo buf, PlannerInfo *root,
List **retrieved_attrs)
{
deparse_expr_cxt context;

elog(DEBUG1, "sqlite_fdw : %s", __func__);

/* Set up context struct for recursion */
Expand Down Expand Up @@ -2653,7 +2656,7 @@ sqlite_deparse_const(Const *node, deparse_expr_cxt *context, int showtype)
appendStringInfo(buf, "X\'%s\'", extval + 2);
break;
case TIMESTAMPOID:
{
{
convert_timestamp_tounixepoch = false;
extval = OidOutputFunctionCall(typoutput, node->constvalue);

Expand All @@ -2675,8 +2678,8 @@ sqlite_deparse_const(Const *node, deparse_expr_cxt *context, int showtype)
sqlite_deparse_string_literal(buf, extval);
}
break;
case UUIDOID:
/* always deparse to BLOB because this is internal PostgreSQL storage
case UUIDOID:
/* always deparse to BLOB because this is internal PostgreSQL storage
* the string for BYTEA always seems to be in the format "\\x##"
* where # is a hex digit, Even if the value passed in is
* 'hi'::bytea we will receive "\x6869". Making this assumption
Expand Down
File renamed without changes.
Loading