diff --git a/packages/datalib/src/TableGridDisplay.ts b/packages/datalib/src/TableGridDisplay.ts index a8507e8e1..840566b9f 100644 --- a/packages/datalib/src/TableGridDisplay.ts +++ b/packages/datalib/src/TableGridDisplay.ts @@ -1,5 +1,5 @@ import GridDisplay from './GridDisplay'; -import { Select, treeToSql, dumpSqlSelect, createColumnResultField } from '@dbgate/sqltree'; +import { Select, treeToSql, dumpSqlSelect } from '@dbgate/sqltree'; import { TableInfo, EngineDriver } from '@dbgate/types'; import GridConfig from './GridConfig'; @@ -15,20 +15,26 @@ export default class TableGridDisplay extends GridDisplay { } createSelect() { + const orderColumnName = this.table.columns[0].columnName; const select: Select = { commandType: 'select', - from: { - source: { name: this.table }, - }, - columns: this.table.columns.map(col => createColumnResultField(col.columnName)), + from: { name: this.table }, + columns: this.table.columns.map(col => ({ exprType: 'column', ...col })), + orderBy: [ + { + exprType: 'column', + columnName: orderColumnName, + direction: 'ASC', + }, + ], }; return select; } getPageQuery(offset: number, count: number) { const select = this.createSelect(); - if (this.driver.dialect.limitSelect) select.topRecords = count; if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count }; + else if (this.driver.dialect.limitSelect) select.topRecords = count; const sql = treeToSql(this.driver, select, dumpSqlSelect); return sql; } diff --git a/packages/engines/mssql/index.js b/packages/engines/mssql/index.js index 2c5f3a78d..4e60c439c 100644 --- a/packages/engines/mssql/index.js +++ b/packages/engines/mssql/index.js @@ -5,6 +5,8 @@ const MsSqlDumper = require("./MsSqlDumper"); /** @type {import('@dbgate/types').SqlDialect} */ const dialect = { limitSelect: true, + rangeSelect: true, + offsetFetchRangeSyntax: true, quoteIdentifier(s) { return `[${s}]`; } diff --git a/packages/sqltree/src/createFunctions.ts b/packages/sqltree/src/createFunctions.ts deleted file mode 100644 index 8baa2e2b7..000000000 --- a/packages/sqltree/src/createFunctions.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ResultField } from './types'; - -export function createColumnResultField(columnName: string): ResultField { - return { - expr: { - exprType: 'column', - columnName, - }, - }; -} diff --git a/packages/sqltree/src/dumpSqlCommand.ts b/packages/sqltree/src/dumpSqlCommand.ts index d866f3751..ae8d19e03 100644 --- a/packages/sqltree/src/dumpSqlCommand.ts +++ b/packages/sqltree/src/dumpSqlCommand.ts @@ -18,15 +18,32 @@ export function dumpSqlSelect(dmp: SqlDumper, select: Select) { if (select.selectAll) dmp.put('&n,'); dmp.put('&>&n'); dmp.putCollection(',&n', select.columns, fld => { - dumpSqlExpression(dmp, fld.expr); + dumpSqlExpression(dmp, fld); if (fld.alias) dmp.put(' %i', fld.alias); }); dmp.put('&n&<'); } dmp.put('^from '); dumpSqlFromDefinition(dmp, select.from); + if (select.groupBy) { + dmp.put('&ngroup ^by '); + dmp.putCollection(', ', select.groupBy, expr => dumpSqlExpression(dmp, expr)); + dmp.put('&n'); + } + if (select.orderBy) { + dmp.put('&n^order ^by '); + dmp.putCollection(', ', select.orderBy, expr => { + dumpSqlExpression(dmp, expr); + dmp.put(' %k', expr.direction); + }); + dmp.put('&n'); + } if (select.range) { - dmp.put('^limit %s ^offset %s ', select.range.limit, select.range.offset); + if (dmp.dialect.offsetFetchRangeSyntax) { + dmp.put('^offset %s ^rows ^fetch ^next %s ^rows ^only', select.range.offset, select.range.limit); + } else { + dmp.put('^limit %s ^offset %s ', select.range.limit, select.range.offset); + } } } diff --git a/packages/sqltree/src/dumpSqlSource.ts b/packages/sqltree/src/dumpSqlSource.ts index a41e67d8e..e096419ef 100644 --- a/packages/sqltree/src/dumpSqlSource.ts +++ b/packages/sqltree/src/dumpSqlSource.ts @@ -41,7 +41,7 @@ export function dumpSqlSourceRef(dmp: SqlDumper, source: Source) { export function dumpSqlRelation(dmp: SqlDumper, from: Relation) { dmp.put('&n %k ', from.joinType); - dumpSqlSourceDef(dmp, from.source); + dumpSqlSourceDef(dmp, from); if (from.conditions) { dmp.put(' ^on '); dmp.putCollection(' ^and ', from.conditions, cond => dumpSqlCondition(dmp, cond)); @@ -49,7 +49,7 @@ export function dumpSqlRelation(dmp: SqlDumper, from: Relation) { } export function dumpSqlFromDefinition(dmp: SqlDumper, from: FromDefinition) { - dumpSqlSourceDef(dmp, from.source); + dumpSqlSourceDef(dmp, from); dmp.put(' '); if (from.relations) from.relations.forEach(rel => dumpSqlRelation(dmp, rel)); } diff --git a/packages/sqltree/src/index.ts b/packages/sqltree/src/index.ts index c575e705b..65e17d74d 100644 --- a/packages/sqltree/src/index.ts +++ b/packages/sqltree/src/index.ts @@ -3,4 +3,3 @@ export * from './dumpSqlCommand'; export * from './treeToSql'; export * from './dumpSqlSource'; export * from './dumpSqlCondition'; -export * from './createFunctions'; diff --git a/packages/sqltree/src/types.ts b/packages/sqltree/src/types.ts index 8256b0a3a..6899382d6 100644 --- a/packages/sqltree/src/types.ts +++ b/packages/sqltree/src/types.ts @@ -13,6 +13,8 @@ export interface Select { range?: RangeDefinition; distinct?: boolean; selectAll?: boolean; + orderBy?: OrderByExpression[]; + groupBy?: Expression[]; } export type Command = Select; @@ -47,16 +49,11 @@ export interface Source { export type JoinType = 'LEFT JOIN' | 'INNER JOIN' | 'RIGHT JOIN'; -export interface Relation { - source: Source; +export type Relation = Source & { conditions: Condition[]; joinType: JoinType; -} - -export interface FromDefinition { - source: Source; - relations?: Relation[]; -} +}; +export type FromDefinition = Source & { relations?: Relation[] }; // export interface Expression { // exprType: "column" | "value" | "string" | "literal" | "count"; @@ -74,8 +71,6 @@ export interface ValueExpression { } export type Expression = ColumnRefExpression | ValueExpression; +export type OrderByExpression = Expression & { direction: 'ASC' | 'DESC' }; -export interface ResultField { - expr: ValueExpression | ColumnRefExpression; - alias?: string; -} +export type ResultField = Expression & { alias?: string }; diff --git a/packages/types/dialect.d.ts b/packages/types/dialect.d.ts index e0c5b06ac..7c8b5bf5a 100644 --- a/packages/types/dialect.d.ts +++ b/packages/types/dialect.d.ts @@ -1,5 +1,6 @@ export interface SqlDialect { rangeSelect?: boolean; limitSelect?: boolean; + offsetFetchRangeSyntax?: boolean; quoteIdentifier(s: string): string; } diff --git a/packages/types/dumper.d.ts b/packages/types/dumper.d.ts index 55d726534..1b39b1d1f 100644 --- a/packages/types/dumper.d.ts +++ b/packages/types/dumper.d.ts @@ -1,7 +1,9 @@ import { TableInfo } from "./dbinfo"; +import { SqlDialect } from "./dialect"; export interface SqlDumper { s: string; + dialect: SqlDialect; putRaw(s: string); put(format: string, ...args);