Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ packages/server/dist/*
**/*/package-lock.json
**/*/yarn.lock
yarn.lock
*.sqlite3
3 changes: 2 additions & 1 deletion .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@
**/*/jest.config.js
**/*/yarn-error.log
README.md
**/*/.npmignore
**/*/.npmignore
!./node_modules/sqlite3
26 changes: 22 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,13 @@ There are two ways to use configuration files.
"user": "postgres",
"password": "pg_pass",
"database": "pg_test",
"projectPaths": ["/Users/joe-re/src/postgres_ptoject"]
"projectPaths": ["/Users/joe-re/src/postgres_project"]
},
{
"name": "sqlite3-project",
"adapter": "sqlite3",
"filename": "/Users/noguchimasato/src/sql-language-server/packages/server/test.sqlite3",
"projectPaths": ["/Users/joe-re/src/sqlite2_project"]
}
]
}
Expand All @@ -105,12 +111,13 @@ Please restart sql-language-server process after create .sqlrc.json.
| Key | Description | value | required | default |
| ------------ | ------------------------------------------------------------------------------------------------------------------------- | ----------------------- | -------- | --------------------------------- |
| name | Connection name(free-form text) | | true | |
| adapter | Database type | "mysql" #124; "postgres" | true | |
| host | Database host | string | true | |
| adapter | Database type | "mysql" #124; "postgres" #124; "sqlite3" | true | |
| host | Database host | string | false | |
| port | Database port | string | false | mysql:3306, postgres:5432 |
| user | Database user | string | true | mysql:"root", postgres:"postgres" |
| user | Database user | string | false | mysql:"root", postgres:"postgres" |
| password | Database password | string | false | |
| database | Database name | string | false | |
| filename | Database filename(only for sqlite3) | string | false | |
| projectPaths | Project path that you want to apply(if you don't set it configuration will not apply automatically when lsp's started up) | string[] | false | [] |
| ssh | Settings for port fowarding | \*see below SSH section | false | |

Expand Down Expand Up @@ -237,6 +244,17 @@ command: switchDataBaseConnection
arguments: string(project name)
```


#### SQLite3 Notes

If you get error when you use sqlite3 connection, you may need to rebuild sqlite3 on your environment.

VSC extension provides the command to rebuild it.(Name: `Rebuild SQLite3 Client`)
![image](https://user-images.githubusercontent.com/4954534/85928359-ef952180-b8de-11ea-8cb3-7a9a509cd6d7.png)

If you're using sql-language-server directly, after go to the directry of it and call `npm rebuild sqlite` to rebuild it.


#### Lint

You can use lint rules that are provided [sqlint](https://github.com/joe-re/sql-language-server/blob/master/packages/sqlint/README.md).
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"watch": "run-p watch:client watch:server",
"watch:client": "cd ./packages/client && yarn run watch",
"watch:server": "cd ./packages/server && yarn run watch",
"vscode:prepublish": "yarn run compile"
"vscode:prepublish": "yarn run compile && rm -rf node_modules/lib/binding"
},
"engines": {
"vscode": "^1.45.1"
Expand All @@ -40,6 +40,11 @@
"command": "extension.fixAllFixableProblems",
"title": "Fix all auto-fixable problems",
"category": "SQLLanguageServer"
},
{
"command": "extension.rebuildSqlite3",
"title": "Rebuild SQLite3 Client",
"category": "SQLLanguageServer"
}
]
},
Expand Down
20 changes: 20 additions & 0 deletions packages/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
TransportKind,
} from 'vscode-languageclient'
import { ExecuteCommandParams } from 'vscode-languageserver-protocol'
import { rebuild } from './rebuild'

export function activate(context: ExtensionContext) {
let serverModule = context.asAbsolutePath(path.join('packages', 'server', 'dist', 'cli.js'))
Expand Down Expand Up @@ -67,8 +68,27 @@ export function activate(context: ExtensionContext) {
client.sendRequest('workspace/executeCommand', params)
})

let isRebuilding = false
const rebuildSqlite3 = commands.registerCommand('extension.rebuildSqlite3', async () => {
if (isRebuilding) {
Window.showInformationMessage('Already started rebuild Sqlite3 process')
return
}
isRebuilding = true
try {
Window.showInformationMessage('Start to rebuild Sqlite3.')
await rebuild()
Window.showInformationMessage('Done to rebuild Sqlite3.')
} catch (e) {
Window.showErrorMessage(e)
} finally {
isRebuilding = false
}
})

context.subscriptions.push(switchConnection)
context.subscriptions.push(fixAllFixableProblem)
context.subscriptions.push(rebuildSqlite3)
context.subscriptions.push(disposable)
client.onReady().then(() => {
client.onNotification('sqlLanguageServer.finishSetup', (params) => {
Expand Down
1 change: 1 addition & 0 deletions packages/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
],
"dependencies": {
"@types/node": "12.12.6",
"electron-rebuild": "^1.11.0",
"typescript": "^3.9.2",
"vscode-languageclient": "^6.1.3",
"vscode-test": "^1.3.0"
Expand Down
6 changes: 6 additions & 0 deletions packages/client/rebuild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import electronRebuild from 'electron-rebuild'

const electronVersion = (process.versions as any).electron
export function rebuild(): Promise<void> {
return electronRebuild({ buildPath: `${__dirname}/../../../node_modules/sqlite3`, electronVersion, force: true, useCache: false })
}
16 changes: 9 additions & 7 deletions packages/server/SettingStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ export type SSHConfig = {
}
export type Settings = {
name: string | null,
adapter: 'mysql' | 'postgresql' | null,
host: string | null,
port: number | null,
user: string | null,
database: string | null,
password: string | null,
projectPaths: string[],
adapter: 'mysql' | 'postgresql' | 'sqlite3' | null,
host: string | null
port: number | null
user: string | null
database: string | null
password: string | null
filename: string | null // for sqlite3
projectPaths: string[]
ssh: SSHConfig | null
}

Expand Down Expand Up @@ -55,6 +56,7 @@ export default class SettingStore extends EventEmitter {
database: null,
password: null,
ssh: null,
filename: null,
projectPaths: []
}
private static instance: SettingStore;
Expand Down
38 changes: 27 additions & 11 deletions packages/server/createServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import getDatabaseClient from './database_libs/getDatabaseClient'
import initializeLogging from './initializeLogging'
import { lint, LintResult } from 'sqlint'
import log4js from 'log4js'
import { RequireSqlite3Error } from './database_libs/Sqlite3Client'

export type ConnectionMethod = 'node-ipc' | 'stdio'
type Args = {
Expand All @@ -41,18 +42,33 @@ export default function createServer() {
connection.onInitialize((params): InitializeResult => {
logger.debug(`onInitialize: ${params.rootPath}`)
SettingStore.getInstance().on('change', async () => {
logger.debug('onInitialize: receive change event from SettingStore')
try {
const client = getDatabaseClient(SettingStore.getInstance().getSetting())
schema = await client.getSchema()
logger.debug('get schema')
logger.debug(JSON.stringify(schema))
connection.sendNotification('sqlLanguageServer.finishSetup', {
personalConfig: SettingStore.getInstance().getPersonalConfig(),
config: SettingStore.getInstance().getSetting()
})
} catch (e) {
logger.error(e)
}
setTimeout(() => {
connection.sendNotification('sqlLanguageServer.finishSetup', {
personalConfig: SettingStore.getInstance().getPersonalConfig(),
config: SettingStore.getInstance().getSetting()
})
}, 1000) // TODO: Need to think about better way to sendNotification
try {
const client = getDatabaseClient(
SettingStore.getInstance().getSetting()
)
schema = await client.getSchema()
logger.debug("get schema")
logger.debug(JSON.stringify(schema))
} catch (e) {
logger.error("failed to get schema info")
if (e instanceof RequireSqlite3Error) {
connection.sendNotification('sqlLanguageServer.error', {
message: "Need to rebuild sqlite3 module."
})
}
throw e
}
} catch (e) {
logger.error(e)
}
})
if (params.rootPath) {
SettingStore.getInstance().setSettingFromFile(
Expand Down
7 changes: 5 additions & 2 deletions packages/server/database_libs/AbstractClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default abstract class AbstractClient {

constructor(protected settings: Settings) {}

abstract connect(): void
abstract connect(): boolean
abstract disconnect(): void
abstract getTables(): Promise<string[]>
abstract getColumns(tableName: string): Promise<RawField[]>
Expand Down Expand Up @@ -57,7 +57,10 @@ export default abstract class AbstractClient {
return []
})
}
this.connect()
if (!this.connect()) {
logger.error('AbstractClinet.getSchema: failed to connect database')
return []
}
try {
const tables = await this.getTables()
schema = await Promise.all(
Expand Down
1 change: 1 addition & 0 deletions packages/server/database_libs/MysqlClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default class MysqlClient extends AbstractClient {
port: this.settings.port || this.DefaultPort,
database: this.settings.database || ''
})
return true
}

disconnect() {
Expand Down
1 change: 1 addition & 0 deletions packages/server/database_libs/PostgresClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export default class PosgresClient extends AbstractClient {
})
client.connect()
this.connection = client
return true
}

disconnect() {
Expand Down
102 changes: 102 additions & 0 deletions packages/server/database_libs/Sqlite3Client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { Settings } from '../SettingStore'
import AbstractClient, { RawField } from './AbstractClient'
import { sqlite3 as SQLite3, Database } from 'sqlite3'
import log4js from 'log4js'

const logger = log4js.getLogger()

export class RequireSqlite3Error extends Error {
constructor(message: any) {
super(message)
this.name = "RequireSQLite3Error"
}
}

export default class Sqlite3Client extends AbstractClient {
connection: Database | null = null

get DefaultPort() { return 0 }
get DefaultHost() { return '' }
get DefaultUser() { return '' }

constructor(settings: Settings) {
super(settings)
}

connect(): boolean {
if (!this.settings.filename) {
throw new Error('Need to specify filename to use sqlite3 connection.')
}
try {
// use commonjs to avoid dynamic import build error
const sqlite3: SQLite3 = require('sqlite3')

this.connection = new sqlite3.Database(
this.settings.filename,
sqlite3.OPEN_READONLY
)
} catch (e) {
logger.error('Sqlite3Client: failed to connect to database', e)
throw new RequireSqlite3Error(e)
}
return true
}

disconnect() {
if (this.connection) {
this.connection.close()
}
this.connection = null
}

getTables(): Promise<string[]> {
const sql = `SELECT name FROM sqlite_master WHERE type='table'`
return new Promise((resolve, reject) => {
if (!this.connection) {
reject(new Error("Don't have database connection."))
return
}
this.connection.all(sql, (err, rows: { name: string}[]) => {
if (err) {
reject(new Error(err.message))
return
}
logger.debug('Sqlite3Clinet: done to get table names', rows)
const tables = rows.map(v => v.name)
resolve(tables)
})
})
}

getColumns(tableName: string): Promise<RawField[]> {
const sql = `SELECT * FROM pragma_table_info('${tableName}')`
return new Promise((resolve, reject) => {
if (!this.connection) {
reject(new Error("Don't have database connection."))
return
}
this.connection.all(sql, (err, rows: {
cld: number
name: string
type: string
notnull: number
dflt_value: any
pk: number
}[]) => {
if (err) {
reject(new Error(err.message))
return
}
logger.debug('Sqlite3Clinet: done to get column names', rows)
const columns: RawField[] = rows.map(v => ({
field: v.name,
type: v.type,
null: v.notnull ? 'Yes' : 'No',
default: v.dflt_value,
comment: v.pk ? 'PRIMARY KEY' : ''
}))
resolve(columns)
})
})
}
}
2 changes: 2 additions & 0 deletions packages/server/database_libs/getDatabaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import AbstractClient from './AbstractClient'
import MysqlClient from './MysqlClient'
import PostgresClient from './PostgresClient'
import { Settings } from '../SettingStore'
import Sqlite3Client from './Sqlite3Client'

export default function getDatabaseClient(settings: Settings): AbstractClient {
switch (settings.adapter) {
case 'mysql': return new MysqlClient(settings)
case 'postgresql': return new PostgresClient(settings)
case 'sqlite3': return new Sqlite3Client(settings)
default: throw new Error(`not support ${settings.adapter}`)
}
}
2 changes: 2 additions & 0 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"mysql": "^2.15.0",
"node-ssh-forward": "^0.6.3",
"pg": "^7.4.3",
"sqlite3": "^4.2.0",
"vscode-languageclient": "^6.1.3",
"vscode-languageserver": "^6.1.1",
"vscode-languageserver-textdocument": "^1.0.1",
Expand All @@ -37,6 +38,7 @@
"@types/jest": "^25.2.2",
"@types/mysql": "^2.15.5",
"@types/node": "^8.10.0",
"@types/sqlite3": "^3.1.6",
"jest": "^26.0.1",
"rollup": "^2.10.2",
"ts-jest": "^26.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/sql-parser/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export type InsertStatement = {
table: string
columns: string[]
values: ValuesClause
location: NodeRange
}

export type ValuesClause = {
Expand Down