Changeset: d70e05683053 for MonetDB URL: https://dev.monetdb.org/hg/MonetDB/rev/d70e05683053 Modified Files: clients/odbc/tests/ODBCmetadata.c Branch: default Log Message:
merged with sep2022 diffs (truncated from 473 to 300 lines): diff --git a/clients/odbc/driver/SQLSpecialColumns.c b/clients/odbc/driver/SQLSpecialColumns.c --- a/clients/odbc/driver/SQLSpecialColumns.c +++ b/clients/odbc/driver/SQLSpecialColumns.c @@ -165,7 +165,7 @@ MNDBSpecialColumns(ODBCStmt *stmt, size_t querylen; /* determine if we need to add a query against the tmp.* tables */ - bool addTmpQuery = (SchemaName == NULL) + bool inclTmpKey = (SchemaName == NULL) || (SchemaName != NULL && (strcmp((const char *) SchemaName, "tmp") == 0 || strchr((const char *) SchemaName, '%') != NULL @@ -205,37 +205,141 @@ MNDBSpecialColumns(ODBCStmt *stmt, } /* construct the query */ - querylen = 5000 + (sch ? strlen(sch) : 0) + (tab ? strlen(tab) : 0); - if (addTmpQuery) - querylen *= 2; + querylen = 6000 + (sch ? strlen(sch) : 0) + (tab ? strlen(tab) : 0); query = malloc(querylen); if (query == NULL) goto nomem; + /* When there is a PK for the table we return the pkey columns. + * When there is No PK but there are multiple unique constraints, we need to pick one. + * In the current implementation we return the first uc (lowest sys.keys.id). + * When there is no PK or unique constraints and it is not a + * view, we return all the columns of the table. + * + * Instead of the first uc (in case of multiple) we could potentially use the uc which has + * a) the least number of columns and + * b) the most efficient datatype (integers) or smallest total(size in bytes). + * That's much more complex to do in SQL than the current implementation. + * The current implementation (picking first uc) is fast and + * gives a correct result, hence preferred. + */ + + /* 1st cte: syskeys */ + pos += strcpy_len(query + pos, + "with syskeys as (" + /* all pkeys */ + "SELECT \"id\", \"table_id\" FROM \"sys\".\"keys\" WHERE \"type\" = 0 " + "UNION ALL " + /* and first unique constraint of a table when table has no pkey */ + "SELECT \"id\", \"table_id\" FROM \"sys\".\"keys\" WHERE \"type\" = 1 " + "AND \"table_id\" NOT IN (select \"table_id\" from \"sys\".\"keys\" where \"type\" = 0) " + "AND (\"table_id\", \"id\") IN (select \"table_id\", min(\"id\") from \"sys\".\"keys\" where \"type\" = 1 group by \"table_id\"))", + querylen - pos); + if (inclTmpKey) { + /* we must also include the primary key or unique constraint of local temporary tables which are stored in tmp.keys */ + /* 2nd cte: tmpkeys */ + pos += strcpy_len(query + pos, + ", tmpkeys as (" + "SELECT \"id\", \"table_id\" FROM \"tmp\".\"keys\" WHERE \"type\" = 0 " + "UNION ALL " + "SELECT \"id\", \"table_id\" FROM \"tmp\".\"keys\" WHERE \"type\" = 1 " + "AND \"table_id\" NOT IN (select \"table_id\" from \"tmp\".\"keys\" where \"type\" = 0) " + "AND (\"table_id\", \"id\") IN (select \"table_id\", min(\"id\") from \"tmp\".\"keys\" where \"type\" = 1 group by \"table_id\"))", + querylen - pos); + } + /* 3rd cte: tableids */ + pos += strcpy_len(query + pos, + ", tableids as (" + "SELECT t.\"id\" " + "FROM \"sys\".\"tables\" t " + "JOIN \"sys\".\"schemas\" s ON t.\"schema_id\" = s.\"id\" " + "WHERE t.\"type\" NOT IN (1, 11)", /* exclude all VIEWs and SYSTEM VIEWs */ + querylen - pos); + /* add the selection condition */ + if (NameLength1 > 0 && CatalogName != NULL) { + /* filtering requested on catalog name */ + if (strcmp((char *) CatalogName, stmt->Dbc->dbname) != 0) { + /* catalog name does not match the database name, so return no rows */ + pos += strcpy_len(query + pos, " and 1=2", querylen - pos); + } + } + if (sch) { + /* filtering requested on schema name */ + pos += snprintf(query + pos, querylen - pos, " and %s", sch); + } + if (tab) { + /* filtering requested on table name */ + pos += snprintf(query + pos, querylen - pos, " and %s", tab); + } + /* 4th cte: cols, this unions 2 (or 4 when inclTmpKey == true) select queries */ + pos += strcpy_len(query + pos, + "), cols as (" + "SELECT c.\"name\", c.\"type\", c.\"type_digits\", c.\"type_scale\", o.\"nr\" " + "FROM syskeys k " + "JOIN tableids t ON k.\"table_id\" = t.\"id\" " + "JOIN \"sys\".\"objects\" o ON k.\"id\" = o.\"id\" " + "JOIN \"sys\".\"_columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\")", + querylen - pos); + /* add an extra selection when SQL_NO_NULLS is requested */ + if (Nullable == SQL_NO_NULLS) { + pos += strcpy_len(query + pos, " WHERE c.\"null\" = false", querylen - pos); + } + if (inclTmpKey) { + /* we must also include the primary key or unique constraint of local temporary tables + * which are stored in tmp.keys, tmp.objects, tmp._tables and tmp._columns */ + pos += strcpy_len(query + pos, + " UNION ALL " + "SELECT c.\"name\", c.\"type\", c.\"type_digits\", c.\"type_scale\", o.\"nr\" " + "FROM tmpkeys k " + "JOIN tableids t ON k.\"table_id\" = t.\"id\" " + "JOIN \"tmp\".\"objects\" o ON k.\"id\" = o.\"id\" " + "JOIN \"tmp\".\"_columns\" c ON (k.\"table_id\" = c.\"table_id\" AND o.\"name\" = c.\"name\")", + querylen - pos); + /* add an extra selection when SQL_NO_NULLS is requested */ + if (Nullable == SQL_NO_NULLS) { + pos += strcpy_len(query + pos, " WHERE c.\"null\" = false", querylen - pos); + } + } + /* when there is No PK and No unique constraints, we should return all columns of the table */ + pos += strcpy_len(query + pos, + " UNION ALL " + "SELECT c.\"name\", c.\"type\", c.\"type_digits\", c.\"type_scale\", c.\"number\" " + "FROM tableids t " + "JOIN \"sys\".\"_columns\" c ON t.\"id\" = c.\"table_id\" " + "WHERE t.\"id\" NOT IN (SELECT \"table_id\" FROM \"sys\".\"keys\" WHERE \"type\" in (0, 1))", + querylen - pos); + /* add an extra selection when SQL_NO_NULLS is requested */ + if (Nullable == SQL_NO_NULLS) { + pos += strcpy_len(query + pos, " AND c.\"null\" = false", querylen - pos); + } + if (inclTmpKey) { + pos += strcpy_len(query + pos, + " UNION ALL " + "SELECT c.\"name\", c.\"type\", c.\"type_digits\", c.\"type_scale\", c.\"number\" " + "FROM tableids t " + "JOIN \"tmp\".\"_columns\" c ON t.\"id\" = c.\"table_id\" " + "WHERE t.\"id\" NOT IN (SELECT \"table_id\" FROM \"tmp\".\"keys\" WHERE \"type\" in (0, 1))", + querylen - pos); + /* add an extra selection when SQL_NO_NULLS is requested */ + if (Nullable == SQL_NO_NULLS) { + pos += strcpy_len(query + pos, " AND c.\"null\" = false", querylen - pos); + } + } + /* the final select query */ /* Note: SCOPE is SQL_SCOPE_TRANSACTION */ /* Note: PSEUDO_COLUMN is SQL_PC_NOT_PSEUDO */ pos += snprintf(query + pos, querylen - pos, - "with sc as (" - "select t.id as table_id, k.type as type, " - "cast(%d as smallint) as scope, " - "c.name as column_name, " + ") SELECT " + "cast(%d AS smallint) AS \"SCOPE\", " + "c.\"name\" AS \"COLUMN_NAME\", " DATA_TYPE(c) ", " TYPE_NAME(c) ", " COLUMN_SIZE(c) ", " BUFFER_LENGTH(c) ", " DECIMAL_DIGITS(c) ", " - "cast(%d as smallint) as pseudo_column " - "from sys.schemas s, " - "sys._tables t, " - "sys._columns c, " - "sys.keys k, " - "sys.objects kc " - "where s.id = t.schema_id and " - "t.id = c.table_id and " - "t.id = k.table_id and " - "c.name = kc.name and " - "kc.id = k.id and " - "k.type in (0, 1)", /* primary key (type = 0), unique key (type = 1) */ + "cast(%d AS smallint) AS \"PSEUDO_COLUMN\" " + "FROM cols c " + "ORDER BY \"SCOPE\", c.\"nr\", \"COLUMN_NAME\"", /* scope: */ SQL_SCOPE_TRANSACTION, #ifdef DATA_TYPE_ARGS @@ -255,168 +359,12 @@ MNDBSpecialColumns(ODBCStmt *stmt, #endif /* pseudo_column: */ SQL_PC_NOT_PSEUDO); - assert(pos < 4300); - /* TODO: improve the SQL to get the correct result: - - only one set of unique constraint columns should be - returned when multiple unique constraints are available - for this table. Return the smallest/best one only. - TODO: optimize SQL: - - when no SchemaName is set (see above) also no - filtering on SCHEMA NAME and join with table - SCHEMAS is needed! - */ - - /* add the selection condition */ - if (NameLength1 > 0 && CatalogName != NULL) { - /* filtering requested on catalog name */ - if (strcmp((char *) CatalogName, stmt->Dbc->dbname) != 0) { - /* catalog name does not match the database name, so return no rows */ - pos += snprintf(query + pos, querylen - pos, " and 1=2"); - } - } - if (sch) { - /* filtering requested on schema name */ - pos += snprintf(query + pos, querylen - pos, " and %s", sch); - } - if (tab) { - /* filtering requested on table name */ - pos += snprintf(query + pos, querylen - pos, " and %s", tab); - } - - /* add an extra selection when SQL_NO_NULLS is requested */ - if (Nullable == SQL_NO_NULLS) { - pos += strcpy_len(query + pos, " and c.\"null\" = false", querylen - pos); - } - - pos += strcpy_len(query + pos, - "), " - "tid as (" - "select table_id as tid " - "from sys.keys " - "where type = 0" - ") " - , querylen - pos); - - if (addTmpQuery) { - /* we must also include the primary key or unique - constraint of local temporary tables which are stored - in tmp.keys, tmp.objects, tmp._tables and tmp._columns */ - - /* Note: SCOPE is SQL_SCOPE_TRANSACTION */ - /* Note: PSEUDO_COLUMN is SQL_PC_NOT_PSEUDO */ - pos += snprintf(query + pos, querylen - pos, - ", tmpsc as (" - "select t.id as table_id, k.type as type, " - "cast(%d as smallint) as scope, " - "c.name as column_name, " - DATA_TYPE(c) ", " - TYPE_NAME(c) ", " - COLUMN_SIZE(c) ", " - BUFFER_LENGTH(c) ", " - DECIMAL_DIGITS(c) ", " - "cast(%d as smallint) as pseudo_column " - "from sys.schemas s, " - "tmp._tables t, " - "tmp._columns c, " - "tmp.keys k, " - "tmp.objects kc " - "where s.id = t.schema_id and " - "t.id = c.table_id and " - "t.id = k.table_id and " - "c.name = kc.name and " - "kc.id = k.id and " - "k.type in (0, 1)", /* primary key (type = 0), unique key (type = 1) */ - /* scope: */ - SQL_SCOPE_TRANSACTION, -#ifdef DATA_TYPE_ARGS - DATA_TYPE_ARGS, -#endif -#ifdef TYPE_NAME_ARGS - TYPE_NAME_ARGS, -#endif -#ifdef COLUMN_SIZE_ARGS - COLUMN_SIZE_ARGS, -#endif -#ifdef BUFFER_SIZE_ARGS - BUFFER_SIZE_ARGS, -#endif -#ifdef DECIMAL_DIGITS_ARGS - DECIMAL_DIGITS_ARGS, -#endif - /* pseudo_column: */ - SQL_PC_NOT_PSEUDO); - - /* add the selection condition */ - if (NameLength1 > 0 && CatalogName != NULL) { - /* filtering requested on catalog name */ - if (strcmp((char *) CatalogName, stmt->Dbc->dbname) != 0) { - /* catalog name does not match the database name, so return no rows */ - pos += snprintf(query + pos, querylen - pos, " and 1=2"); - } - } - if (sch) { - /* filtering requested on schema name */ - pos += snprintf(query + pos, querylen - pos, " and %s", sch); - } - if (tab) { - /* filtering requested on table name */ - pos += snprintf(query + pos, querylen - pos, " and %s", tab); - } - - /* add an extra selection when SQL_NO_NULLS is requested */ - if (Nullable == SQL_NO_NULLS) { - pos += strcpy_len(query + pos, " and c.\"null\" = false", querylen - pos); - } - - pos += strcpy_len(query + pos, - "), " - "tmptid as (" - "select table_id as tid " - "from tmp.keys " - "where type = 0" - ") " - , querylen - pos); - } - assert(pos < (querylen - 500)); _______________________________________________ checkin-list mailing list -- checkin-list@monetdb.org To unsubscribe send an email to checkin-list-le...@monetdb.org