Skip to content
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

SAP Hana dialect #215

Merged
merged 35 commits into from May 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a3925fe
initial sap hana support
May 6, 2019
99861f8
sap hana dialect now working
May 7, 2019
38ffa16
feature/edit-connection: Start of #185
mtxr May 2, 2019
9d104cc
master: Fix error decorator
mtxr May 3, 2019
ea3c927
ORA: Add pool configuration (oracleOptions)
mickeypearce May 3, 2019
3929f13
chore(package): update @types/node to version 12.0.0
greenkeeper[bot] May 3, 2019
61e9cce
master: Minor fix on error handler
mtxr May 4, 2019
f7b191f
master: Updated yarn.lock
mtxr May 4, 2019
4c74900
master: Fixes header scroll issue. Coses #201
mtxr May 5, 2019
ef389f2
Fix syntax for json fields on results. Closes #207
mtxr May 5, 2019
6daa2bf
master: Add support for MSSQL funcitons and procedures. Closes #203
mtxr May 5, 2019
6f7b59f
master: Update donators
mtxr May 5, 2019
011437c
feature/edit-connection: Revert to MS language server api
mtxr May 5, 2019
3596384
feature/edit-connection: Basic updating already working
mtxr May 6, 2019
ed7b4df
feature/edit-connection: Enhance bg on setup screen
mtxr May 6, 2019
0c7202e
feature/edit-connection: Fix editing for paths
mtxr May 6, 2019
a5427de
feature/edit-connection: update/create connection ui with new options
mtxr May 7, 2019
5f34ed1
feature/edit-connection: Scroll top on add/update
mtxr May 7, 2019
7a05134
feature/edit-connection: Add edit option. Close #185
mtxr May 7, 2019
e6e0332
master: Bump version to v0.18
mtxr May 7, 2019
2dea462
initial sap hana support
May 6, 2019
93f267a
sap hana dialect now working
May 7, 2019
f376f83
use dependency manager
May 7, 2019
3c29b00
Merge branch 'master' into saphanaDialect
ariel-bentu May 7, 2019
61acf2a
fix yarn.lock conflicts
May 7, 2019
e1560fd
remove hana-client from deliver, rely on dependency manager to instal…
May 8, 2019
6806a3a
Merge branch 'master' into saphanaDialect
ariel-bentu May 8, 2019
9d76299
Update package.json
mtxr May 11, 2019
fd4a922
Update webpack.config.js
mtxr May 11, 2019
5b67289
Update launch.json
mtxr May 11, 2019
292e127
Update index.ts
mtxr May 11, 2019
a696cb6
Merge branch 'master' into saphanaDialect
mtxr May 11, 2019
faf2094
saphanaDialect: Add support for extra args for npm
mtxr May 11, 2019
e22037d
Merge pull request #1 from mtxr/saphanaDialect
ariel-bentu May 11, 2019
db602f9
Update index.ts
mtxr May 11, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Expand Up @@ -16,4 +16,5 @@ cache/
*.db
*.zip
.test-database/oracle/docker-images-master
.test-database/**/local*
.test-database/**/local*
.DS_Store
11 changes: 11 additions & 0 deletions .test-database/.vscode/settings.json
Expand Up @@ -120,5 +120,16 @@
"authProtocol": "xprotocol"
}
},
{
ariel-bentu marked this conversation as resolved.
Show resolved Hide resolved
"askForPassword": false,
"connectionTimeout": 30,
"database": "98D27766B06E45F9A78EA36D5D6C3A5B",
"dialect": "SAPHana",
"name": "Hana",
"password": "Tg6cdRmULar3.yQcw4kVh.SeFNaF._iPaWLFdDvB94_C2jNnVB25reG63nSUkMs.DPFllAJHKwsRCRz1qtgGQrc4t81H8eLpGq3pUsMi9IUrwWZoabdLUHM3FHyhEOee",
"port": 30041,
"server": "127.0.0.1",
"username": "SBSS_51921077446042462816087486121804932303752819285673181439206685123"
}
]
}
3 changes: 2 additions & 1 deletion docs/Connections.md
Expand Up @@ -4,4 +4,5 @@
* [MySQL](Connections/mysql.md)
* [Oracle Database](Connections/OracleDB.md)
* [PostgreSQL](Connections/postgresql.md)
* [SQLite](Connections/SQLite.md)
* [SQLite](Connections/SQLite.md)
* [SAP HANA](Connections/SAPHANA.md)
35 changes: 35 additions & 0 deletions docs/Connections/SAPHANA.md
@@ -0,0 +1,35 @@
# SAP HANA Start Guide

## 1. Prerequisites

> Extension automatically asks to install the SAP HANA driver (@sap/sap-client)

## 2. Connections

### 2.1 SAP HANA Connection

For full reference, see [SAP Documentation](https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.02/en-US/4fe9978ebac44f35b9369ef5a4a26f4c.html)

```json
{
"name": "<your prefered name>",
"dialect": "SAPHana",
"database": "<the schema you want to connect to>",
"username": "<user-name",
"password": "<pwd>",
"connectionTimeout": 15 //in seconds,0 for disabling
}
```


### 2.2 Alternative Connection Strings

ConnectionString maps from `connectString` property:

```json
{
"name": "<your prefered name>",
"dialect": "SAPHana",
"connectString": "<see docs>" // Example: "connectString": "HOST=myServer;PORT=30015;UID=MyUser;PWD=MyPassword"
}
```
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Expand Up @@ -7,6 +7,7 @@
* [Oracle Database](Connections/OracleDB.md)
* [PostgreSQL](Connections/postgresql.md)
* [SQLite](Connections/SQLite.md)
* [SAP HANA](Connections/SAPHANA.md)
* [Executing Queries](executing-queries.md)
* [Codelens](codelens.md)
* [Contributing](Contributing.md)
Expand Down
23 changes: 23 additions & 0 deletions packages/core/.vscode/launch.json
@@ -0,0 +1,23 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "attach",
"name": "Attach by Process ID",
"processId": "${command:PickProcess}"
},
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/index.ts",
"outFiles": [
"${workspaceFolder}/**/*.js"
]
}
]
}
1 change: 1 addition & 0 deletions packages/core/dialect/generic.ts
Expand Up @@ -13,6 +13,7 @@ export interface Deps {
name: string;
version?: string;
env?: { [id: string]: string };
args?: string[], // extra arguments to be passaged to packag managers
}

export default abstract class GenericDialect<ConnectionType extends any> implements ConnectionDialect {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/dialect/index.ts
Expand Up @@ -3,13 +3,14 @@ import MySQL from './mysql';
import OracleDB from './oracle';
import PostgreSQL from './pgsql';
import SQLite from './sqlite';

import SAPHana from './saphana';
const dialects = {
MSSQL,
MySQL,
PostgreSQL,
OracleDB,
SQLite,
SAPHana
};

export default dialects;
5 changes: 5 additions & 0 deletions packages/core/dialect/saphana/debug.js
@@ -0,0 +1,5 @@
module.exports = function (ns){
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this file mandatory? If not, please, consider removing

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, please remove that DS_store file and add it to .gitignore on project root

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no actually this file is left over before move to package manager

return function (args) {
//do nothing
}
};
208 changes: 208 additions & 0 deletions packages/core/dialect/saphana/index.ts
@@ -0,0 +1,208 @@

import {
ConnectionDialect
} from '@sqltools/core/interface';
import * as Utils from '@sqltools/core/utils';
import queries from './queries';
import GenericDialect from '@sqltools/core/dialect/generic';
import { DatabaseInterface } from '@sqltools/core/plugin-api';

interface Statement {
exec(params: any[], handler: (err: any, row: any) => void);
}

interface HanaClientModule {
createConnection(connOptions: object): HanaConnection;
}

interface HanaConnection {
connect(handler: (err: any) => void);
exec(query: string, handler: (err: any, row: any) => void);
disconnect();
prepare(query: string, handler: (err: any, statement: Statement) => void);
}

export default class SAPHana extends GenericDialect<HanaConnection> implements ConnectionDialect {

public static deps: typeof GenericDialect['deps'] = [{
type: 'package',
name: '@sap/hana-client',
version: '2.4.126',
args: ['--@sap:registry=https://npm.sap.com']
}];

private get lib() {
return __non_webpack_require__('@sap/hana-client') as HanaClientModule;
}

queries = queries;
private schema: String;

public open(encrypt?: boolean): Promise<HanaConnection> {
if (this.connection) {
return this.connection;
}

this.needToInstallDependencies();

const connOptions = {
HOST: this.credentials.server,
PORT: this.credentials.port,
UID: this.credentials.username,
PWD: this.credentials.password
}
if (this.credentials.connectionTimeout && this.credentials.connectionTimeout > 0) {
connOptions["CONNECTTIMEOUT"] = this.credentials.connectionTimeout * 1000;
}
if (encrypt) {
connOptions["ENCRYPT"] = true;
}

try {
let conn = this.lib.createConnection(connOptions);

this.connection = new Promise<HanaConnection>((resolve, reject) => conn.connect(err => {
if (err) {
console.error("Connection to HANA failed", err.toString());
reject(err);
}
this.schema = this.credentials.database;
conn.exec("SET SCHEMA " + this.schema, err => {
if (err) {
reject(err);
}
console.log("Connection to SAP Hana succedded!");
resolve(conn);
});
}));
return this.connection;
} catch (e) {
console.error("Connection to HANA failed" + e.toString());
Promise.reject(e);
}
}

public async close() {
if (!this.connection) return Promise.resolve();

await this.connection.then(conn => conn.disconnect());
this.connection = null;
}

public async testConnection?() {
return this.open().then(conn => conn.exec('select 1 from dummy;', (err, rows) => rows));
mtxr marked this conversation as resolved.
Show resolved Hide resolved
}

public query(query: string, args?): Promise<DatabaseInterface.QueryResults[]> {
return this.open().then(conn => {
return new Promise<DatabaseInterface.QueryResults[]>((resolve, reject) => {
if (args) {
conn.prepare(query, (err, statement) => {
if (err) {
return this.resolveErr(resolve, err, query);
}
statement.exec(args, (err, rows) => {
if (err) {
return this.resolveErr(resolve, err, query);
}
return this.resolveQueryResults(resolve, rows, query);
});
});
} else {
conn.exec(query, (err, rows) => {
if (err) {
return this.resolveErr(resolve, err, query);
}
return this.resolveQueryResults(resolve, rows, query);
});
}
});
});
}

private resolveQueryResults(resolve, rows, query) {
let cols: string[] = [];
if (rows && rows.length > 0) {
for (let colName in rows[0]) {
cols.push(colName);
}
}

let res = {
connId: this.getId(),
results: rows,
cols: cols,
query: query,
messages: []
} as DatabaseInterface.QueryResults

return resolve([res]);
}

private resolveErr(resolve, err, query) {
let messages: string[] = [];
if (err.message) {
messages.push(err.message);
}

return resolve([{
connId: this.getId(),
error: err,
results: [],
cols: [],
query: query,
messages: messages
} as DatabaseInterface.QueryResults]);
}

public getTables(): Promise<DatabaseInterface.Table[]> {
return this.query(this.queries.fetchTables, [this.schema])
.then(([queryRes]) => {
return queryRes.results
.reduce((prev, curr) => prev.concat(curr), [])
.map((obj) => {
return {
name: obj.TABLENAME,
isView: !!obj.ISVIEW,
numberOfColumns: parseInt(obj.NUMBEROFCOLUMNS, 10),
tableCatalog: obj.TABLECATALOG,
tableDatabase: obj.DBNAME,
tableSchema: obj.TABLESCHEMA,
tree: obj.TREE,
} as DatabaseInterface.Table;
});
});
}

public getColumns(): Promise<DatabaseInterface.TableColumn[]> {
return this.query(this.queries.fetchColumns, [this.schema])
.then(([queryRes]) => {
return queryRes.results
.reduce((prev, curr) => prev.concat(curr), [])
.map((obj) => {
return {
columnName: obj.COLUMNNAME,
defaultValue: obj.DEFAULTVALUE,
isNullable: !!obj.ISNULLABLE ? obj.ISNULLABLE.toString() === 'yes' : null,
size: obj.SIZE !== null ? parseInt(obj.SIZE, 10) : null,
tableCatalog: obj.TABLECATALOG,
tableDatabase: obj.DBNAME,
tableName: obj.TABLENAME,
tableSchema: obj.TABLESCHEMA,
isPk: (obj.KEYTYPE || '').toLowerCase() === 'primary key',
isFk: (obj.KEYTYPE || '').toLowerCase() === 'foreign key',
type: obj.TYPE,
tree: obj.TREE,
} as DatabaseInterface.TableColumn;
});
});
}

public async getFunctions() {
return [];
}

public describeTable(prefixedTable: string) {
return this.query(this.queries.describeTable, [this.schema, prefixedTable]);
}
}