enhanced SQLSpecialColumns

Quan Zongliang <[email protected]> Wed, 14 Nov 2018 21:48:01 +0800
Newsgroups gmane.comp.db.postgresql.odbc
Message-ID <[email protected]>
hi, all

patch attched,please review.

If a table has not WITH OIDS option.
Try to find primary-key(prefered)/unique index.
If it has not either of them, returns "ctid" as text type.

--
Quan Zongliang
specialcols.patch (text/plain, 7.1 KB)
diff --git a/info.c b/info.c
index 052f0ae..a6e126e 100644
--- a/info.c
+++ b/info.c
@@ -2988,21 +2988,121 @@ MYLOG(DETAIL_LOG_LEVEL, "Add ctid\n");
 			Int2	the_type = PG_TYPE_OID;
 			int	atttypmod = -1;
 
-			if (relhasoids[0] != '1')
+			if (relhasoids[0] == '1')
 			{
-				ret = SQL_SUCCESS;
-				goto cleanup;
+				tuple = QR_AddNew(res);
+
+				set_tuplefield_int2(&tuple[SPECOLS_SCOPE], SQL_SCOPE_SESSION);
+				set_tuplefield_string(&tuple[SPECOLS_COLUMN_NAME], OID_NAME);
+				set_tuplefield_int2(&tuple[SPECOLS_DATA_TYPE], PGTYPE_ATTR_TO_CONCISE_TYPE(conn, the_type, atttypmod));
+				set_tuplefield_string(&tuple[SPECOLS_TYPE_NAME], pgtype_attr_to_name(conn, the_type, atttypmod, TRUE));
+				set_tuplefield_int4(&tuple[SPECOLS_COLUMN_SIZE], PGTYPE_ATTR_COLUMN_SIZE(conn, the_type, atttypmod));
+				set_tuplefield_int4(&tuple[SPECOLS_BUFFER_LENGTH], PGTYPE_ATTR_BUFFER_LENGTH(conn, the_type, atttypmod));
+				set_tuplefield_int2(&tuple[SPECOLS_DECIMAL_DIGITS], PGTYPE_ATTR_DECIMAL_DIGITS(conn, the_type, atttypmod));
+				set_tuplefield_int2(&tuple[SPECOLS_PSEUDO_COLUMN], SQL_PC_PSEUDO);
 			}
-			tuple = QR_AddNew(res);
+			else
+			{
+				OID conoid = InvalidOid;
+				QResultClass *rowid_res;
+				SQLULEN col_num;
+
+				/* From 9.5, PG has array_position(). */
+				/* Do not intend to support 9.4 and earlier */
+				if (conn->pg_version_major<9 ||
+						(conn->pg_version_major==9 && conn->pg_version_minor<5))
+				{
+					ret = SQL_SUCCESS;
+					goto cleanup;
+				}
 
-			set_tuplefield_int2(&tuple[SPECOLS_SCOPE], SQL_SCOPE_SESSION);
-			set_tuplefield_string(&tuple[SPECOLS_COLUMN_NAME], OID_NAME);
-			set_tuplefield_int2(&tuple[SPECOLS_DATA_TYPE], PGTYPE_ATTR_TO_CONCISE_TYPE(conn, the_type, atttypmod));
-			set_tuplefield_string(&tuple[SPECOLS_TYPE_NAME], pgtype_attr_to_name(conn, the_type, atttypmod, TRUE));
-			set_tuplefield_int4(&tuple[SPECOLS_COLUMN_SIZE], PGTYPE_ATTR_COLUMN_SIZE(conn, the_type, atttypmod));
-			set_tuplefield_int4(&tuple[SPECOLS_BUFFER_LENGTH], PGTYPE_ATTR_BUFFER_LENGTH(conn, the_type, atttypmod));
-			set_tuplefield_int2(&tuple[SPECOLS_DECIMAL_DIGITS], PGTYPE_ATTR_DECIMAL_DIGITS(conn, the_type, atttypmod));
-			set_tuplefield_int2(&tuple[SPECOLS_PSEUDO_COLUMN], SQL_PC_PSEUDO);
+				resetPQExpBuffer(&columns_query);
+
+				appendPQExpBufferStr(&columns_query, "SELECT c.oid, c.contype, a.attname, a.atttypid, a.atttypmod");
+				appendPQExpBufferStr(&columns_query, " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class r");
+				appendPQExpBufferStr(&columns_query, ", pg_constraint c, pg_attribute a ");
+				appendPQExpBufferStr(&columns_query, "WHERE n.oid = r.relnamespace AND c.conrelid = r.oid ");
+				appendPQExpBufferStr(&columns_query, "AND (contype = 'p' OR contype = 'u') ");
+				appendPQExpBufferStr(&columns_query, "AND a.attrelid = r.oid AND a.attnum = ANY(c.conkey) ");
+				if (escTableName)
+					appendPQExpBuffer(&columns_query,
+								"AND r.relname %s'%s' ", eq_string, escTableName);
+
+				schema_appendPQExpBuffer1(&columns_query, "AND n.nspname %s'%.*s' ", eq_string, escSchemaName,
+										TABLE_IS_VALID(szTableName, cbTableName), conn);
+				/* "ORDER BY c.contype" keep primary key first(if exists) */
+				appendPQExpBufferStr(&columns_query, "ORDER BY c.contype,array_position(c.conkey, a.attnum)");
+
+				if (PQExpBufferDataBroken(columns_query))
+				{
+					SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "Out of memory in PGAPI_SpecialColumns()", func);
+					goto cleanup;
+				}
+
+				rowid_res = CC_send_query(conn, columns_query.data, NULL, READ_ONLY_QUERY, stmt);
+				if (!QR_command_maybe_successful(rowid_res))
+				{
+					SC_set_error(stmt, STMT_EXEC_ERROR, "PGAPI_SpecialColumns query error", func);
+					QR_Destructor(rowid_res);
+					goto cleanup;
+				}
+
+				/* has primary key */
+				col_num = QR_get_num_total_tuples(rowid_res);
+				if (col_num > 0)
+				{
+					SQLULEN i;
+					OID f_oid;
+					OID f_type;
+					int f_mod;
+
+					for (i=0; i<col_num; i++)
+					{
+						f_oid = QR_get_value_backend_oid(rowid_res, i, 0, NULL);
+						/* only returns first constraint */
+						if (conoid == InvalidOid)
+							conoid = f_oid;
+						else if (f_oid != conoid)
+							break;
+
+						f_type = QR_get_value_backend_int(rowid_res, i, 3, NULL);
+						f_mod = QR_get_value_backend_int(rowid_res, i, 4, NULL);
+
+						tuple = QR_AddNew(res);
+
+						set_tuplefield_int2(&tuple[SPECOLS_SCOPE], SQL_SCOPE_TRANSACTION);
+						set_tuplefield_string(&tuple[SPECOLS_COLUMN_NAME], QR_get_value_backend_text(rowid_res, i, 2));
+						set_tuplefield_int2(&tuple[SPECOLS_DATA_TYPE], PGTYPE_ATTR_TO_CONCISE_TYPE(conn, f_type, f_mod));
+						set_tuplefield_string(&tuple[SPECOLS_TYPE_NAME], pgtype_attr_to_name(conn, f_type, f_mod, FALSE));
+						set_tuplefield_int4(&tuple[SPECOLS_COLUMN_SIZE], PGTYPE_ATTR_COLUMN_SIZE(conn, f_type, f_mod));
+						set_tuplefield_int4(&tuple[SPECOLS_BUFFER_LENGTH], PGTYPE_ATTR_BUFFER_LENGTH(conn, f_type, f_mod));
+						set_tuplefield_int2(&tuple[SPECOLS_DECIMAL_DIGITS], PGTYPE_ATTR_DECIMAL_DIGITS(conn, f_type, f_mod));
+						set_tuplefield_int2(&tuple[SPECOLS_PSEUDO_COLUMN], SQL_PC_NOT_PSEUDO);
+					}
+
+					QR_Destructor(rowid_res);
+					ret = SQL_SUCCESS;
+					goto cleanup;
+				}
+
+				QR_Destructor(rowid_res);
+
+				the_type = PG_TYPE_TEXT;
+				atttypmod = -1;
+				tuple = QR_AddNew(res);
+
+				set_tuplefield_int2(&tuple[SPECOLS_SCOPE], SQL_SCOPE_CURROW);
+				set_tuplefield_string(&tuple[SPECOLS_COLUMN_NAME], CTID_NAME);
+				set_tuplefield_int2(&tuple[SPECOLS_DATA_TYPE], PGTYPE_ATTR_TO_CONCISE_TYPE(conn, the_type, atttypmod));
+				set_tuplefield_string(&tuple[SPECOLS_TYPE_NAME], pgtype_attr_to_name(conn, the_type, atttypmod, FALSE));
+				set_tuplefield_int4(&tuple[SPECOLS_COLUMN_SIZE], PGTYPE_ATTR_COLUMN_SIZE(conn, the_type, atttypmod));
+				set_tuplefield_int4(&tuple[SPECOLS_BUFFER_LENGTH], PGTYPE_ATTR_BUFFER_LENGTH(conn, the_type, atttypmod));
+				set_tuplefield_int2(&tuple[SPECOLS_DECIMAL_DIGITS], PGTYPE_ATTR_DECIMAL_DIGITS(conn, the_type, atttypmod));
+				set_tuplefield_int2(&tuple[SPECOLS_PSEUDO_COLUMN], SQL_PC_PSEUDO);
+			}
+
+			ret = SQL_SUCCESS;
+			goto cleanup;
 		}
 		else if (fColType == SQL_ROWVER)
 		{
diff --git a/psqlodbc.h b/psqlodbc.h
index fad41e0..5a027c6 100644
--- a/psqlodbc.h
+++ b/psqlodbc.h
@@ -678,6 +678,7 @@ CSTR	PRINT_NULL = "(null)";
 #define OID_NAME "oid"
 #define XMIN_NAME "xmin"
 #define TABLEOID_NAME "tableoid"
+#define CTID_NAME "ctid"
 
 enum
 {
diff --git a/qresult.h b/qresult.h
index 652b4b7..e335020 100644
--- a/qresult.h
+++ b/qresult.h
@@ -133,6 +133,7 @@ enum {
 #define QR_get_value_backend_row(self, tupleno, fieldno) ((self->backend_tuples + (tupleno * self->num_fields))[fieldno].value)
 #define QR_get_value_backend_text(self, tupleno, fieldno) QR_get_value_backend_row(self, tupleno, fieldno)
 #define QR_get_value_backend_int(self, tupleno, fieldno, isNull) atoi(QR_get_value_backend_row(self, tupleno, fieldno))
+#define QR_get_value_backend_oid(self, tupleno, fieldno, isNull) atooid(QR_get_value_backend_row(self, tupleno, fieldno))
 
 /*	These functions are used by both manual and backend results */
 #define QR_NumResultCols(self)		(CI_get_num_fields(self->fields))