diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a3c475 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- +/freetds +/.vs +/*.vcx* +/*.sln +/x64 +/*.user +/*.opendb +/*.db diff --git a/include/options.h b/include/options.h index 08c2d12..743420c 100644 --- a/include/options.h +++ b/include/options.h @@ -17,6 +17,7 @@ typedef struct TdsFdwOption typedef struct TdsFdwOptionSet { char *servername; + char *instance_name; char *language; char *character_set; int port; diff --git a/include/tds_fdw.h b/include/tds_fdw.h index 11b3b87..0dff627 100644 --- a/include/tds_fdw.h +++ b/include/tds_fdw.h @@ -40,8 +40,10 @@ #include "options.h" + #if PG_VERSION_NUM >= 90500 #define IMPORT_API +#define IGNORE_DEFAULT_COLUMN_VALUES //this flag disable default col values for CREATE FOREIGN TABLE syntax due to the use of built-in functions for the default value. (fails on IMPORT FOREIGN SCHEMA...) #else #undef IMPORT_API #endif /* PG_VERSION_NUM */ @@ -127,8 +129,8 @@ typedef struct /* functions called via SQL */ -extern Datum tds_fdw_handler(PG_FUNCTION_ARGS); -extern Datum tds_fdw_validator(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum tds_fdw_handler(PG_FUNCTION_ARGS); +extern PGDLLEXPORT Datum tds_fdw_validator(PG_FUNCTION_ARGS); /* FDW callback routines */ diff --git a/src/deparse.c b/src/deparse.c index e60203e..2807af6 100644 --- a/src/deparse.c +++ b/src/deparse.c @@ -764,7 +764,6 @@ deparseSelectSql(StringInfo buf, /* * Construct FROM clause */ - appendStringInfoString(buf, " FROM "); deparseRelation(buf, rel); heap_close(rel, NoLock); @@ -1244,12 +1243,21 @@ deparseRelation(StringInfo buf, Relation rel) if (relname == NULL) relname = RelationGetRelationName(rel);*/ - if (nspname == NULL) - appendStringInfo(buf, "%s", - relname); - else - appendStringInfo(buf, "%s.%s", - tds_quote_identifier(nspname), tds_quote_identifier(relname)); + + if (relname != NULL) { + appendStringInfoString(buf, " FROM "); + if (nspname == NULL) + appendStringInfo(buf, "%s", + relname); + else + appendStringInfo(buf, "%s.%s", + tds_quote_identifier(nspname), tds_quote_identifier(relname)); + } + else { + // from clause miss. query like this: "select current_date() as curdt" + } + + } /* diff --git a/src/options.c b/src/options.c index c19db7a..3ba3fb9 100644 --- a/src/options.c +++ b/src/options.c @@ -51,6 +51,7 @@ void tdsOptionSetInit(TdsFdwOptionSet* option_set); static struct TdsFdwOption valid_options[] = { { "servername", ForeignServerRelationId }, + { "instance_name", ForeignServerRelationId }, { "language", ForeignServerRelationId }, { "character_set", ForeignServerRelationId }, { "port", ForeignServerRelationId }, @@ -94,7 +95,7 @@ static const int DEFAULT_MATCH_COLUMN_NAMES = 1; /* by default we use remote estimates */ -static const int DEFAULT_USE_REMOTE_ESTIMATE = 1; +static const int DEFAULT_USE_REMOTE_ESTIMATE = 0; //! https://www.postgresql.org/docs/9.3/static/postgres-fdw.html The default is false. otherwise, it triggers a triple same query execution /* by default we use remote estimates */ @@ -260,6 +261,17 @@ void tdsGetForeignServerOptions(List *options_list, TdsFdwOptionSet *option_set) option_set->servername = defGetString(def); } + else if (strcmp(def->defname, "instance_name") == 0) + { + if (option_set->instance_name) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("Redundant option: instance_name (%s)", defGetString(def)) + )); + + option_set->instance_name = defGetString(def); + } + else if (strcmp(def->defname, "language") == 0) { @@ -737,6 +749,26 @@ void tdsSetDefaultOptions(TdsFdwOptionSet *option_set) )); #endif } + + if (!option_set->instance_name) + { + char * inst_name = ""; + if ((option_set->instance_name = palloc((strlen(inst_name) + 1) * sizeof(char))) == NULL) + { + ereport(ERROR, + (errcode(ERRCODE_FDW_OUT_OF_MEMORY), + errmsg("Failed to allocate memory for instance_name") + )); + } + + sprintf(option_set->instance_name, "%s", inst_name); + +#ifdef DEBUG + ereport(NOTICE, + (errmsg("Set instance_name to default: %s", option_set->instance_name) + )); +#endif + } if (!option_set->row_estimate_method) { @@ -917,6 +949,7 @@ void tdsOptionSetInit(TdsFdwOptionSet* option_set) #endif option_set->servername = NULL; + option_set->instance_name = NULL; option_set->language = NULL; option_set->character_set = NULL; option_set->port = 0; diff --git a/src/tds_fdw.c b/src/tds_fdw.c index e615264..2199515 100644 --- a/src/tds_fdw.c +++ b/src/tds_fdw.c @@ -68,6 +68,7 @@ #include #include + /* #define DEBUG */ PG_MODULE_MAGIC; @@ -76,6 +77,8 @@ PG_MODULE_MAGIC; #include "options.h" #include "deparse.h" + + /* run on module load */ extern PGDLLEXPORT void _PG_init(void); @@ -517,11 +520,15 @@ int tdsSetupConnection(TdsFdwOptionSet* option_set, LOGINREC *login, DBPROCESS * conn_string = palloc((strlen(option_set->servername) + 10) * sizeof(char)); - if (option_set->port) - { - sprintf(conn_string, "%s:%i", option_set->servername, option_set->port); - } - + if (strlen(option_set->instance_name)) + { + sprintf(conn_string, "%s\\%s", option_set->servername, option_set->instance_name); + } + else if (option_set->port) + { + sprintf(conn_string, "%s:%i", option_set->servername, option_set->port); + } + else { sprintf(conn_string, "%s", option_set->servername); @@ -1407,7 +1414,8 @@ void tdsGetColumnMetadata(ForeignScanState *node, TdsFdwOptionSet *option_set) (errmsg("tds_fdw: Comparing retrieved column name to the following local column name: %s", local_name) )); - if (strncmp(local_name, column->name, NAMEDATALEN) == 0) + //! \PPV local_name always in lowcase. But column->name's register defined in sql server. so you should ignore case + if (strnicmp(local_name, column->name, NAMEDATALEN) == 0) { ereport(DEBUG3, (errmsg("tds_fdw: It matches!") @@ -1440,7 +1448,9 @@ void tdsGetColumnMetadata(ForeignScanState *node, TdsFdwOptionSet *option_set) )); column->local_index = ncol; - column->attr_oid = festate->attinmeta->tupdesc->attrs[ncol]->atttypid; + //!\PPV fix crash on index miss! + if(ncol < festate->attinmeta->tupdesc->natts) + column->attr_oid = festate->attinmeta->tupdesc->attrs[ncol]->atttypid; } ereport(DEBUG3, @@ -3228,9 +3238,16 @@ tdsImportSqlServerSchema(ImportForeignSchemaStmt *stmt, DBPROCESS *dbproc, deparseStringLiteral(&buf, column_name); appendStringInfoChar(&buf, ')'); - /* Add DEFAULT if needed */ + +#ifndef IGNORE_DEFAULT_COLUMN_VALUES + /* Add DEFAULT if needed */ if (import_default && column_default[0] != '\0') appendStringInfo(&buf, " DEFAULT %s", column_default); +#endif /* PG_VERSION_NUM */ + + + + /* Add NOT NULL if needed */ if (import_not_null && strcmp(is_nullable, "NO") == 0)