SQLite Forum

Feature Request : Get table name alias from prepared statement
Login
really kinda silly - someone spent some time making this super easy to extend, and noone actually wants to use that ability :) 

This is just for reference, I understand there's apparently a difference between open source and open contribution; I don't care if you don't use my exact bytes to provide the functionality; be contrarian.

https://github.com/d3x0r/sqlite/tree/sqlite3_column_table_alias (which has a git PR representing the diff between master and it; not this is relevant information)


xx.diff

``` diff
 src/loadext.c    |   6 ++
 src/select.c     |  25 +++++---
 src/sqlite.h.in  |   7 ++-
 src/sqlite3ext.h |   6 ++
 src/test1.c      |   2 +
 src/vdbe.h       |  13 ++--
 src/vdbeapi.c    |  17 +++++
 test/capi3f.test | 188 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 8 files changed, 249 insertions(+), 15 deletions(-)

diff --git a/src/loadext.c b/src/loadext.c
index 4edefec0c..0fb2c41e5 100644
--- a/src/loadext.c
+++ b/src/loadext.c
@@ -30,6 +30,8 @@
 # define sqlite3_column_database_name16 0
 # define sqlite3_column_table_name      0
 # define sqlite3_column_table_name16    0
+# define sqlite3_column_table_alias     0
+# define sqlite3_column_table_alias16   0
 # define sqlite3_column_origin_name     0
 # define sqlite3_column_origin_name16   0
 #endif
@@ -61,6 +63,7 @@
 # define sqlite3_value_text16le         0
 # define sqlite3_column_database_name16 0
 # define sqlite3_column_table_name16    0
+# define sqlite3_column_table_alias16   0
 # define sqlite3_column_origin_name16   0
 #endif
 
@@ -485,6 +488,9 @@ static const sqlite3_api_routines sqlite3Apis = {
   sqlite3_total_changes64,
   /* Version 3.37.0 and later */
   sqlite3_autovacuum_pages,
+  /* Version 3.38.0 and later */
+  sqlite3_column_table_alias,
+  sqlite3_column_table_alias16,
 };
 
 /* True if x is the directory separator character
diff --git a/src/select.c b/src/select.c
index ebb746467..e50b42c49 100644
--- a/src/select.c
+++ b/src/select.c
@@ -1751,9 +1751,9 @@ static void generateSortTail(
 ** the SQLITE_ENABLE_COLUMN_METADATA compile-time option is used.
 */
 #ifdef SQLITE_ENABLE_COLUMN_METADATA
-# define columnType(A,B,C,D,E) columnTypeImpl(A,B,C,D,E)
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F)
 #else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
-# define columnType(A,B,C,D,E) columnTypeImpl(A,B)
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B)
 #endif
 static const char *columnTypeImpl(
   NameContext *pNC, 
@@ -1763,7 +1763,8 @@ static const char *columnTypeImpl(
   Expr *pExpr,
   const char **pzOrigDb,
   const char **pzOrigTab,
-  const char **pzOrigCol
+  const char **pzOrigCol,
+  const char **pzOrigTabAlias
 #endif
 ){
   char const *zType = 0;
@@ -1772,6 +1773,7 @@ static const char *columnTypeImpl(
   char const *zOrigDb = 0;
   char const *zOrigTab = 0;
   char const *zOrigCol = 0;
+  char const *zOrigTabAlias = 0;
 #endif
 
   assert( pExpr!=0 );
@@ -1791,6 +1793,10 @@ static const char *columnTypeImpl(
         if( j<pTabList->nSrc ){
           pTab = pTabList->a[j].pTab;
           pS = pTabList->a[j].pSelect;
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+          zOrigTabAlias =
+              pTabList->a[j].zAlias?pTabList->a[j].zAlias:pTabList->a[j].zName;
+#endif
         }else{
           pNC = pNC->pNext;
         }
@@ -1839,7 +1845,7 @@ static const char *columnTypeImpl(
           sNC.pSrcList = pS->pSrc;
           sNC.pNext = pNC;
           sNC.pParse = pNC->pParse;
-          zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol); 
+          zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol,&zOrigTabAlias);
         }
       }else{
         /* A real table or a CTE table */
@@ -1885,7 +1891,7 @@ static const char *columnTypeImpl(
       sNC.pSrcList = pS->pSrc;
       sNC.pNext = pNC;
       sNC.pParse = pNC->pParse;
-      zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol); 
+      zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, &zOrigTabAlias);
       break;
     }
 #endif
@@ -1897,6 +1903,7 @@ static const char *columnTypeImpl(
     *pzOrigDb = zOrigDb;
     *pzOrigTab = zOrigTab;
     *pzOrigCol = zOrigCol;
+    *pzOrigTabAlias = zOrigTabAlias;
   }
 #endif
   return zType;
@@ -1925,7 +1932,8 @@ static void generateColumnTypes(
     const char *zOrigDb = 0;
     const char *zOrigTab = 0;
     const char *zOrigCol = 0;
-    zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
+    const char *zOrigTabAlias = 0;
+    zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, &zOrigTabAlias);
 
     /* The vdbe must make its own copy of the column-type and other 
     ** column specific strings, in case the schema is reset before this
@@ -1934,8 +1942,9 @@ static void generateColumnTypes(
     sqlite3VdbeSetColName(v, i, COLNAME_DATABASE, zOrigDb, SQLITE_TRANSIENT);
     sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
     sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
+    sqlite3VdbeSetColName(v, i, COLNAME_TABLE_ALIAS, zOrigTabAlias, SQLITE_TRANSIENT);
 #else
-    zType = columnType(&sNC, p, 0, 0, 0);
+    zType = columnType(&sNC, p, 0, 0, 0, 0);
 #endif
     sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
   }
@@ -2199,7 +2208,7 @@ void sqlite3SelectAddColumnTypeAndCollation(
     int n, m;
     pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
     p = a[i].pExpr;
-    zType = columnType(&sNC, p, 0, 0, 0);
+    zType = columnType(&sNC, p, 0, 0, 0, 0);
     /* pCol->szEst = ... // Column size est for SELECT tables never used */
     pCol->affinity = sqlite3ExprAffinity(p);
     if( zType ){
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 98a028b0b..d86391bce 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -4656,7 +4656,10 @@ const void *sqlite3_column_name16(sqlite3_stmt*, int N);
 ** ^The name of the database or table or column can be returned as
 ** either a UTF-8 or UTF-16 string.  ^The _database_ routines return
 ** the database name, the _table_ routines return the table name, and
-** the origin_ routines return the column name.
+** the origin_ routines return the column name.  _table_alias_ results
+** with the alias(if any) of the table associated with the column;
+** _table_ always returns the source table name, even if it has been
+** aliased, this returns the original table name if there is no alias.
 ** ^The returned string is valid until the [prepared statement] is destroyed
 ** using [sqlite3_finalize()] or until the statement is automatically
 ** reprepared by the first call to [sqlite3_step()] for a particular run
@@ -4694,6 +4697,8 @@ const char *sqlite3_column_table_name(sqlite3_stmt*,int);
 const void *sqlite3_column_table_name16(sqlite3_stmt*,int);
 const char *sqlite3_column_origin_name(sqlite3_stmt*,int);
 const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
+const char *sqlite3_column_table_alias(sqlite3_stmt*,int);
+const void *sqlite3_column_table_alias16(sqlite3_stmt*,int);
 
 /*
 ** CAPI3REF: Declared Datatype Of A Query Result
diff --git a/src/sqlite3ext.h b/src/sqlite3ext.h
index 9767daa01..cecb80691 100644
--- a/src/sqlite3ext.h
+++ b/src/sqlite3ext.h
@@ -344,6 +344,9 @@ struct sqlite3_api_routines {
   int (*autovacuum_pages)(sqlite3*,
      unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
      void*, void(*)(void*));
+  /* Version 3.38.0 and later */
+  const char * (*column_table_alias)(sqlite3_stmt*,int);
+  const void * (*column_table_alias16)(sqlite3_stmt*,int);
 };
 
 /*
@@ -655,6 +658,9 @@ typedef int (*sqlite3_loadext_entry)(
 #define sqlite3_total_changes64        sqlite3_api->total_changes64
 /* Version 3.37.0 and later */
 #define sqlite3_autovacuum_pages       sqlite3_api->autovacuum_pages
+/* Version 3.38.0 and later */
+#define sqlite3_column_table_alias     sqlite3_api->column_table_alias
+#define sqlite3_column_table_alias16   sqlite3_api->column_table_alias16
 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
 
 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/src/test1.c b/src/test1.c
index 6060290b9..c0d8a4682 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -8506,6 +8506,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
 #ifdef SQLITE_ENABLE_COLUMN_METADATA
 { "sqlite3_column_database_name",test_stmt_utf8,(void*)sqlite3_column_database_name},
 { "sqlite3_column_table_name",test_stmt_utf8,(void*)sqlite3_column_table_name},
+{ "sqlite3_column_table_alias",test_stmt_utf8,(void*)sqlite3_column_table_alias},
 { "sqlite3_column_origin_name",test_stmt_utf8,(void*)sqlite3_column_origin_name},
 #endif
 
@@ -8521,6 +8522,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
 {"sqlite3_column_database_name16",
   test_stmt_utf16, (void*)sqlite3_column_database_name16},
 {"sqlite3_column_table_name16", test_stmt_utf16, (void*)sqlite3_column_table_name16},
+{"sqlite3_column_table_alias16", test_stmt_utf16, (void*)sqlite3_column_table_alias16},
 {"sqlite3_column_origin_name16", test_stmt_utf16, (void*)sqlite3_column_origin_name16},
 #endif
 #endif
diff --git a/src/vdbe.h b/src/vdbe.h
index 3257ff68a..1ee2b66e3 100644
--- a/src/vdbe.h
+++ b/src/vdbe.h
@@ -140,13 +140,14 @@ typedef struct VdbeOpList VdbeOpList;
 ** The Vdbe.aColName array contains 5n Mem structures, where n is the 
 ** number of columns of data returned by the statement.
 */
-#define COLNAME_NAME     0
-#define COLNAME_DECLTYPE 1
-#define COLNAME_DATABASE 2
-#define COLNAME_TABLE    3
-#define COLNAME_COLUMN   4
+#define COLNAME_NAME        0
+#define COLNAME_DECLTYPE    1
+#define COLNAME_DATABASE    2
+#define COLNAME_TABLE       3
+#define COLNAME_COLUMN      4
+#define COLNAME_TABLE_ALIAS 5
 #ifdef SQLITE_ENABLE_COLUMN_METADATA
-# define COLNAME_N        5      /* Number of COLNAME_xxx symbols */
+# define COLNAME_N          6      /* Number of COLNAME_xxx symbols */
 #else
 # ifdef SQLITE_OMIT_DECLTYPE
 #   define COLNAME_N      1      /* Store only the name */
diff --git a/src/vdbeapi.c b/src/vdbeapi.c
index 5eeb5d1c0..6548b6a85 100644
--- a/src/vdbeapi.c
+++ b/src/vdbeapi.c
@@ -1321,6 +1321,23 @@ const void *sqlite3_column_origin_name16(sqlite3_stmt *pStmt, int N){
   return columnName(pStmt, N, 1, COLNAME_COLUMN);
 }
 #endif /* SQLITE_OMIT_UTF16 */
+
+/*
+** Return the alias or, if no alias specified, the name of the table from
+** which a result column derives. NULL is returned if the result name is
+** an expression or constant or anything else which is not an unambiguous
+** reference to a database table.
+*/
+const char *sqlite3_column_table_alias(sqlite3_stmt *pStmt, int N){
+  return columnName(
+      pStmt, N, 0, COLNAME_TABLE_ALIAS);
+}
+#ifndef SQLITE_OMIT_UTF16
+const void *sqlite3_column_table_alias16(sqlite3_stmt *pStmt, int N){
+  return columnName(
+      pStmt, N, 1, COLNAME_TABLE_ALIAS);
+}
+#endif /* SQLITE_OMIT_UTF16 */
 #endif /* SQLITE_ENABLE_COLUMN_METADATA */
 
 
diff --git a/test/capi3f.test b/test/capi3f.test
new file mode 100644
index 000000000..ca4efab4c
--- /dev/null
+++ b/test/capi3f.test
@@ -0,0 +1,188 @@
+# 2021 January 22
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.  The
+# focus of this script testing the callback-free C/C++ API.
+#
+# $Id: capi3f.test,v 1.70 2009/01/09 02:49:32 d3x0r Exp $
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set ::testprefix capi3f
+
+# Do not use a codec for tests in this file, as the database file is
+# manipulated directly using tcl scripts (using the [hexio_write] command).
+#
+do_not_use_codec
+
+# Return the UTF-16 representation of the supplied UTF-8 string $str.
+# If $nt is true, append two 0x00 bytes as a nul terminator.
+proc utf16 {str {nt 1}} {
+  set r [encoding convertto unicode $str]
+  if {$nt} {
+    append r "\x00\x00"
+  }
+  return $r
+}
+
+# Return the UTF-8 representation of the supplied UTF-16 string $str. 
+proc utf8 {str} {
+  # If $str ends in two 0x00 0x00 bytes, knock these off before
+  # converting to UTF-8 using TCL.
+  binary scan $str \c* vals
+  if {[lindex $vals end]==0 && [lindex $vals end-1]==0} {
+    set str [binary format \c* [lrange $vals 0 end-2]]
+  }
+
+  set r [encoding convertfrom unicode $str]
+  return $r
+}
+
+
+# This proc is used to test the following API calls:
+#
+# sqlite3_column_origin_name
+# sqlite3_column_origin_name16
+# sqlite3_column_table_name
+# sqlite3_column_table_name16
+# sqlite3_column_database_name
+# sqlite3_column_database_name16
+# sqlite3_column_table_alias
+# sqlite3_column_table_alias16
+
+#
+# $STMT is a compiled SQL statement. $test is a prefix
+# to use for test names within this proc. $names is a list
+# of the column names that should be returned by $STMT.
+# $decltypes is a list of column declaration types for $STMT.
+#
+# Example:
+#
+# set STMT [sqlite3_prepare "SELECT 1, 2, 2;" -1 DUMMY]
+# check_header test1.1 {1 2 3} {"" "" ""}
+#
+proc check_origin_header2 {STMT test dbs tables als cols} {
+  # If sqlite3_column_origin_name() and friends are not compiled into
+  # this build, this proc is a no-op.
+  ifcapable columnmetadata {
+    # Use the return value of sqlite3_column_count() to build
+    # a list of column indexes. i.e. If sqlite3_column_count
+    # is 3, build the list {0 1 2}.
+    set ::idxlist [list]
+    set ::numcols [sqlite3_column_count $STMT]
+    for {set i 0} {$i < $::numcols} {incr i} {lappend ::idxlist $i}
+  
+    # Database names in UTF-8
+    do_test $test.1 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [sqlite3_column_database_name $STMT $i]
+      } 
+      set cnamelist
+    } $dbs
+  
+    # Database names in UTF-16
+    ifcapable {utf16} {
+      do_test $test.2 {
+        set cnamelist [list]
+        foreach i $idxlist {
+          lappend cnamelist [utf8 [sqlite3_column_database_name16 $STMT $i]]
+        }
+        set cnamelist
+      } $dbs
+    }
+  
+    # Table alias in UTF-8
+    do_test $test.3 {
+      set caliaslist [list]
+      foreach i $idxlist {
+        lappend caliaslist [sqlite3_column_table_alias $STMT $i]
+      } 
+      set caliaslist
+    } $als
+  
+    # Table alias in UTF-16
+    ifcapable {utf16} {
+      do_test $test.4 {
+        set caliaslist [list]
+        foreach i $idxlist {
+          lappend caliaslist [utf8 [sqlite3_column_table_alias16 $STMT $i]]
+        }
+        set caliaslist
+      } $als
+    }
+
+    # Table names in UTF-8
+    do_test $test.5 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [sqlite3_column_table_name $STMT $i]
+      } 
+      set cnamelist
+    } $tables
+  
+    # Table names in UTF-16
+    ifcapable {utf16} {
+      do_test $test.6 {
+        set cnamelist [list]
+        foreach i $idxlist {
+          lappend cnamelist [utf8 [sqlite3_column_table_name16 $STMT $i]]
+        }
+        set cnamelist
+      } $tables
+    }
+  
+    # Origin names in UTF-8
+    do_test $test.7 {
+      set cnamelist [list]
+      foreach i $idxlist {
+        lappend cnamelist [sqlite3_column_origin_name $STMT $i]
+      } 
+      set cnamelist
+    } $cols
+  
+    # Origin declaration types in UTF-16
+    ifcapable {utf16} {
+      do_test $test.8 {
+        set cnamelist [list]
+        foreach i $idxlist {
+          lappend cnamelist [utf8 [sqlite3_column_origin_name16 $STMT $i]]
+        }
+        set cnamelist
+      } $cols
+    }
+  }
+}
+
+do_test capi3f-1 {
+  execsql {
+    CREATE TABLE t1(a VARINT, b BLOB, c VARCHAR(16));
+    INSERT INTO t1 VALUES(1, 2, 3);
+    INSERT INTO t1 VALUES('one', 'two', NULL);
+    INSERT INTO t1 VALUES(1.2, 1.3, 1.4);
+  }
+  set sql "SELECT * FROM t1 tz"
+  set STMT [sqlite3_prepare_v2 $DB $sql -1 TAIL]
+  sqlite3_column_count $STMT
+} 3
+
+check_origin_header2 $STMT capi3f-1 {main main main} {t1 t1 t1} {tz tz tz} {a b c}
+
+do_test capi3f-2 {
+  set sql2 "SELECT * FROM t1"
+  set STMT [sqlite3_prepare_v2 $DB $sql2 -1 TAIL]
+  sqlite3_column_count $STMT
+} 3
+
+check_origin_header2 $STMT capi3f-2 {main main main} {t1 t1 t1} {t1 t1 t1} {a b c}
+
+
+finish_test

```