-
Notifications
You must be signed in to change notification settings - Fork 133
Support BCP on TSQL #temp table #4632
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
base: BABEL_5_X_DEV
Are you sure you want to change the base?
Changes from all commits
c621fb9
5029e18
902d340
df64359
51b0660
f2b73fb
ca868a6
aba6f2d
e25e9b6
b8eabe0
ee6121b
6ae335d
d62a003
791a09d
bb7ed6f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6093,4 +6093,3 @@ openxml_simple(PG_FUNCTION_ARGS) | |
| NO_XML_SUPPORT(); | ||
| #endif /* USE_LIBXML */ | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| #include "postgres.h" | ||
|
|
||
| #include "access/heapam.h" | ||
| #include "access/htup_details.h" | ||
| #include "access/skey.h" | ||
| #include "access/table.h" | ||
| #include "catalog/indexing.h" | ||
| #include "catalog/namespace.h" | ||
| #include "catalog/pg_attribute.h" | ||
| #include "funcapi.h" | ||
| #include "miscadmin.h" | ||
| #include "utils/builtins.h" | ||
| #include "utils/fmgroids.h" | ||
| #include "utils/lsyscache.h" | ||
| #include "utils/memutils.h" | ||
| #include "utils/queryenvironment.h" | ||
| #include "utils/tuplestore.h" | ||
|
|
||
| #include "../src/pltsql.h" | ||
| #include "../src/multidb.h" | ||
|
|
||
| PG_FUNCTION_INFO_V1(get_tsql_temp_table_attributes); | ||
| PG_FUNCTION_INFO_V1(is_temp_table_name); | ||
|
|
||
| /* | ||
| * get_tsql_temp_table_attributes - Get pg_attribute rows for a temp table by name | ||
| * | ||
| * This function returns all pg_attribute columns for a temp table. | ||
| * It works for both ENR temp tables (reads from ENR cache) and non-ENR temp tables | ||
| * (reads from actual pg_attribute catalog). | ||
| */ | ||
| Datum | ||
| get_tsql_temp_table_attributes(PG_FUNCTION_ARGS) | ||
| { | ||
| char *input; | ||
| char *db_name; | ||
| char *schema_name; | ||
| char *object_name; | ||
| Oid relid = InvalidOid; | ||
| ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; | ||
| TupleDesc tupdesc; | ||
| Tuplestorestate *tupstore; | ||
| MemoryContext per_query_ctx; | ||
| MemoryContext oldcontext; | ||
| EphemeralNamedRelation enr = NULL; | ||
| Relation pg_attribute_rel; | ||
| bool is_enr = false; | ||
|
|
||
| /* check to see if caller supports us returning a tuplestore */ | ||
| if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) | ||
| ereport(ERROR, | ||
| (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||
| errmsg("set-valued function called in context that cannot accept a set"))); | ||
| if (!(rsinfo->allowedModes & SFRM_Materialize)) | ||
| ereport(ERROR, | ||
| (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||
| errmsg("materialize mode required, but it is not allowed in this context"))); | ||
|
|
||
| if (PG_ARGISNULL(0)) | ||
| PG_RETURN_NULL(); | ||
|
|
||
| input = text_to_cstring(PG_GETARG_VARCHAR_PP(0)); | ||
|
|
||
| /* Parse the table name */ | ||
| downcase_truncate_split_object_name(input, NULL, &db_name, &schema_name, &object_name); | ||
| pfree(input); | ||
|
|
||
| /* Must be a temp table (starts with #) */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To avoid confusion, let's use tsql temp tables |
||
| if (object_name[0] != '#') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. object_name could be empty or null? any chance this could be called from PG dialect? |
||
| { | ||
| pfree(db_name); | ||
| pfree(schema_name); | ||
| pfree(object_name); | ||
|
Comment on lines
+71
to
+73
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. validate pointers before pfree |
||
| PG_RETURN_NULL(); | ||
| } | ||
|
|
||
| /* Try ENR first */ | ||
| if (currentQueryEnv != NULL) | ||
| { | ||
| enr = get_ENR(currentQueryEnv, object_name, true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. have we tested this part of code for following change of commands? TSQL Dialect -> TSQL Proc -> calls get_tsql_temp_table_attributes remember that TSQL temp table can be created at stage in between |
||
| if (enr != NULL && enr->md.enrtype == ENR_TSQL_TEMP) | ||
| { | ||
| is_enr = true; | ||
| relid = enr->md.reliddesc; | ||
| } | ||
| } | ||
|
|
||
| /* If not ENR, try pg_temp namespace for non-ENR temp tables */ | ||
| if (!is_enr) | ||
| { | ||
| Oid temp_ns = LookupNamespaceNoError("pg_temp"); | ||
| if (OidIsValid(temp_ns)) | ||
| relid = get_relname_relid(object_name, temp_ns); | ||
| } | ||
|
|
||
| pfree(db_name); | ||
| pfree(schema_name); | ||
| pfree(object_name); | ||
|
|
||
| if (!OidIsValid(relid)) | ||
| PG_RETURN_NULL(); | ||
|
|
||
| /* Setup return - use pg_attribute's tuple descriptor */ | ||
| per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; | ||
| oldcontext = MemoryContextSwitchTo(per_query_ctx); | ||
|
|
||
| pg_attribute_rel = table_open(AttributeRelationId, AccessShareLock); | ||
| tupdesc = CreateTupleDescCopy(RelationGetDescr(pg_attribute_rel)); | ||
|
|
||
| tupstore = tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random, | ||
| false, work_mem); | ||
|
|
||
| rsinfo->returnMode = SFRM_Materialize; | ||
| rsinfo->setResult = tupstore; | ||
| rsinfo->setDesc = BlessTupleDesc(tupdesc); | ||
|
Comment on lines
+103
to
+115
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there any reason why we are preferring this way of SRF instead of what PG Doc says, https://www.postgresql.org/docs/current/xfunc-c.html#XFUNC-C-RETURN-SET |
||
|
|
||
| MemoryContextSwitchTo(oldcontext); | ||
|
|
||
| if (is_enr) | ||
| { | ||
| /* ENR path: return tuples directly from ENR cache */ | ||
| ListCell *lc; | ||
|
|
||
| foreach(lc, enr->md.cattups[ENR_CATTUP_ATTRIBUTE]) | ||
| { | ||
| HeapTuple tup = (HeapTuple) lfirst(lc); | ||
|
|
||
| tuplestore_puttuple(tupstore, tup); | ||
|
Comment on lines
+126
to
+128
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest to make a copy before puttuple |
||
| } | ||
| } | ||
| else | ||
| { | ||
| /* Non-ENR path: scan pg_attribute catalog */ | ||
| ScanKeyData skey[1]; | ||
| SysScanDesc scan; | ||
| HeapTuple tup; | ||
|
|
||
| ScanKeyInit(&skey[0], | ||
| Anum_pg_attribute_attrelid, | ||
| BTEqualStrategyNumber, F_OIDEQ, | ||
| ObjectIdGetDatum(relid)); | ||
|
|
||
| scan = systable_beginscan(pg_attribute_rel, AttributeRelidNumIndexId, | ||
| true, NULL, 1, skey); | ||
|
|
||
| while (HeapTupleIsValid(tup = systable_getnext(scan))) | ||
| { | ||
| tuplestore_puttuple(tupstore, tup); | ||
| } | ||
|
|
||
| systable_endscan(scan); | ||
| } | ||
|
|
||
| table_close(pg_attribute_rel, AccessShareLock); | ||
|
|
||
| tuplestore_donestoring(tupstore); | ||
|
|
||
| PG_RETURN_NULL(); | ||
| } | ||
|
|
||
| /* | ||
| * is_temp_table_name - Check if a name refers to a temp table | ||
| * | ||
| * Returns true if the object name part starts with #. | ||
| * | ||
| * The input name can be a four-part qualified name, with quoted identifiers | ||
| * or square bracket qualifiers. We use downcase_truncate_split_object_name() | ||
| * to parse and extract the object_name part. | ||
| */ | ||
| Datum | ||
| is_temp_table_name(PG_FUNCTION_ARGS) | ||
| { | ||
| char *input; | ||
| char *db_name; | ||
| char *schema_name; | ||
| char *object_name; | ||
| bool result = false; | ||
|
|
||
| if (PG_ARGISNULL(0)) | ||
| PG_RETURN_BOOL(false); | ||
|
|
||
| input = text_to_cstring(PG_GETARG_VARCHAR_PP(0)); | ||
|
|
||
| /* Parse the name to extract object_name */ | ||
| downcase_truncate_split_object_name(input, NULL, &db_name, &schema_name, &object_name); | ||
| pfree(input); | ||
|
|
||
| /* Check if object_name starts with # */ | ||
| if (object_name[0] == '#') | ||
| result = true; | ||
|
Comment on lines
+188
to
+190
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it fine to make such decision just base on name? we should look into currentQueryEnv. There could be issue with interoperability here since name with |
||
|
|
||
| pfree(db_name); | ||
| pfree(schema_name); | ||
| pfree(object_name); | ||
|
|
||
| PG_RETURN_BOOL(result); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -802,24 +802,69 @@ CREATE OR REPLACE VIEW sys.spt_tablecollations_view AS | |
| c.is_sparse = 0; | ||
| GRANT SELECT ON sys.spt_tablecollations_view TO PUBLIC; | ||
|
|
||
| -- Function to get pg_attribute rows for #temp tables (ENR and non-ENR) | ||
| CREATE OR REPLACE FUNCTION sys.babelfish_get_temp_table_attributes(IN table_name sys.varchar(4000)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for safer side, it should be sys.nvarchar(128) |
||
| RETURNS SETOF pg_catalog.pg_attribute | ||
| AS 'babelfishpg_tsql', 'get_tsql_temp_table_attributes' | ||
| LANGUAGE C STABLE PARALLEL UNSAFE; | ||
| GRANT EXECUTE ON FUNCTION sys.babelfish_get_temp_table_attributes(IN sys.varchar(4000)) TO PUBLIC; | ||
|
|
||
| -- Function to check if a name refers to a #temp table | ||
| CREATE OR REPLACE FUNCTION sys.is_temp_table_name(IN name sys.varchar(4000)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above, use sys.nvarchar |
||
| RETURNS BOOLEAN | ||
| AS 'babelfishpg_tsql', 'is_temp_table_name' | ||
| LANGUAGE C IMMUTABLE PARALLEL SAFE; | ||
| GRANT EXECUTE ON FUNCTION sys.is_temp_table_name(IN sys.varchar(4000)) TO PUBLIC; | ||
|
|
||
| -- Wrapper function for sp_tablecollations_100 that uses the babelfish_get_temp_table_attributes function | ||
| CREATE OR REPLACE FUNCTION sys.sp_tablecollations_100_enr(IN table_name sys.varchar(4000)) | ||
| RETURNS TABLE(colid INT, name sys.varchar, collation_name sys.nvarchar(128)) | ||
| AS $$ | ||
| SELECT | ||
| CAST(a.attnum AS INT) AS colid, | ||
| CAST(a.attname AS sys.varchar) AS name, | ||
| CAST(c.collname AS sys.nvarchar(128)) AS collation_name | ||
| FROM sys.babelfish_get_temp_table_attributes(table_name) a | ||
| LEFT JOIN pg_catalog.pg_collation c ON a.attcollation = c.oid | ||
| WHERE a.attnum > 0 AND NOT a.attisdropped | ||
| ORDER BY a.attnum; | ||
| $$ | ||
| LANGUAGE SQL STABLE PARALLEL UNSAFE; | ||
| GRANT EXECUTE ON FUNCTION sys.sp_tablecollations_100_enr(IN sys.varchar(4000)) TO PUBLIC; | ||
|
|
||
| -- We are limited by what postgres procedures can return here, but IEW may not | ||
| -- need it for initial compatibility | ||
| -- Modified to handle #temp tables using sp_tablecollations_100_enr function | ||
| CREATE OR REPLACE PROCEDURE sys.sp_tablecollations_100 | ||
| ( | ||
| IN "@object" nvarchar(4000) | ||
| ) | ||
| AS $$ | ||
| BEGIN | ||
| select | ||
| s_tcv.colid AS colid, | ||
| s_tcv.name AS name, | ||
| s_tcv.tds_collation_100 AS tds_collation, | ||
| s_tcv.collation_100 AS collation | ||
| from | ||
| sys.spt_tablecollations_view s_tcv | ||
| where | ||
| s_tcv.object_id = (SELECT sys.object_id(@object)) | ||
| order by colid; | ||
| -- Check if this is a #temp table | ||
| IF sys.is_temp_table_name(@object) = 1 | ||
| BEGIN | ||
| -- Use ENR function for temp tables | ||
| SELECT | ||
| t.colid AS colid, | ||
| t.name AS name, | ||
| CAST(CollationProperty(t.collation_name, 'tdscollation') AS sys.binary(5)) AS tds_collation, | ||
| t.collation_name AS collation | ||
| FROM sys.sp_tablecollations_100_enr(@object) t | ||
| ORDER BY t.colid; | ||
| END | ||
| ELSE | ||
| BEGIN | ||
| -- Existing logic for regular tables | ||
| SELECT | ||
| s_tcv.colid AS colid, | ||
| s_tcv.name AS name, | ||
| s_tcv.tds_collation_100 AS tds_collation, | ||
| s_tcv.collation_100 AS collation | ||
| FROM sys.spt_tablecollations_view s_tcv | ||
| WHERE s_tcv.object_id = (SELECT sys.object_id(@object)) | ||
| ORDER BY colid; | ||
| END | ||
| END; | ||
| $$ | ||
| LANGUAGE 'pltsql'; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |
| #include "utils/rel.h" | ||
| #include "utils/syscache.h" | ||
| #include "utils/fmgroids.h" | ||
| #include "utils/lsyscache.h" | ||
| #include "utils/formatting.h" | ||
| #include "pltsql_instr.h" | ||
| #include "pltsql.h" | ||
|
|
@@ -55,6 +56,7 @@ | |
| #include "catalog.h" | ||
| #include "catalog/toasting.h" | ||
| #include "extendedproperty.h" | ||
| #include "collation.h" | ||
| #include "multidb.h" | ||
| #include "session.h" | ||
| #include "rolecmds.h" | ||
|
|
@@ -5115,4 +5117,4 @@ tsql_openxml_get_colpattern(PG_FUNCTION_ARGS) | |
| } | ||
|
|
||
| PG_RETURN_TEXT_P(cstring_to_text(xpath_expr)); | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. revert file back to original |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we need db_name? we can pass NULL instead