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

Reply via email to