diff --git a/packages/datalib/src/FormViewDisplay.ts b/packages/datalib/src/FormViewDisplay.ts index 406fc583c..48ccdd907 100644 --- a/packages/datalib/src/FormViewDisplay.ts +++ b/packages/datalib/src/FormViewDisplay.ts @@ -1,6 +1,14 @@ import _ from 'lodash'; import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc } from './GridConfig'; -import { ForeignKeyInfo, TableInfo, ColumnInfo, EngineDriver, NamedObjectInfo, DatabaseInfo } from 'dbgate-types'; +import { + ForeignKeyInfo, + TableInfo, + ColumnInfo, + EngineDriver, + NamedObjectInfo, + DatabaseInfo, + SqlDialect, +} from 'dbgate-types'; import { parseFilter, getFilterType, getFilterValueExpression } from 'dbgate-filterparser'; import { filterName } from './filterName'; import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet'; @@ -12,6 +20,7 @@ export class FormViewDisplay { isLoadedCorrectly = true; columns: DisplayColumn[]; public baseTable: TableInfo; + dialect: SqlDialect; constructor( public config: GridConfig, @@ -19,8 +28,11 @@ export class FormViewDisplay { public cache: GridCache, protected setCache: ChangeCacheFunc, public driver?: EngineDriver, - public dbinfo: DatabaseInfo = null - ) {} + public dbinfo: DatabaseInfo = null, + public serverVersion = null + ) { + this.dialect = (driver?.dialectByVersion && driver?.dialectByVersion(serverVersion)) || driver?.dialect; + } addFilterColumn(column) { if (!column) return; diff --git a/packages/datalib/src/GridDisplay.ts b/packages/datalib/src/GridDisplay.ts index b22010edb..00fb8031c 100644 --- a/packages/datalib/src/GridDisplay.ts +++ b/packages/datalib/src/GridDisplay.ts @@ -8,6 +8,7 @@ import { NamedObjectInfo, DatabaseInfo, CollectionInfo, + SqlDialect, } from 'dbgate-types'; import { parseFilter, getFilterType } from 'dbgate-filterparser'; import { filterName } from './filterName'; @@ -60,8 +61,12 @@ export abstract class GridDisplay { public cache: GridCache, protected setCache: ChangeCacheFunc, public driver?: EngineDriver, - public dbinfo: DatabaseInfo = null - ) {} + public dbinfo: DatabaseInfo = null, + public serverVersion = null + ) { + this.dialect = (driver?.dialectByVersion && driver?.dialectByVersion(serverVersion)) || driver?.dialect; + } + dialect: SqlDialect; columns: DisplayColumn[]; baseTable?: TableInfo; baseCollection?: CollectionInfo; @@ -460,12 +465,75 @@ export abstract class GridDisplay { return select; } + getRowNumberOverSelect(select: Select, offset: number, count: number): Select { + const innerSelect: Select = { + commandType: 'select', + from: select.from, + where: select.where, + columns: [ + ...select.columns, + { + alias: '_rowNumber', + exprType: 'rowNumber', + orderBy: select.orderBy + ? select.orderBy.map(x => + x.exprType != 'column' + ? x + : x.source + ? x + : { + ...x, + source: { alias: 'basetbl' }, + } + ) + : [ + { + ...select.columns[0], + direction: 'ASC', + }, + ], + }, + ], + }; + + const res: Select = { + commandType: 'select', + selectAll: true, + from: { + subQuery: innerSelect, + alias: '_RowNumberResult', + }, + where: { + conditionType: 'between', + expr: { + exprType: 'column', + columnName: '_RowNumber', + source: { + alias: '_RowNumberResult', + }, + }, + left: { + exprType: 'value', + value: offset + 1, + }, + right: { + exprType: 'value', + value: offset + count, + }, + }, + }; + + return res; + } + getPageQuery(offset: number, count: number) { if (!this.driver) return null; - const select = this.createSelect(); + let select = this.createSelect(); if (!select) return null; - if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count }; - else if (this.driver.dialect.limitSelect) select.topRecords = count; + if (this.dialect.rangeSelect) select.range = { offset: offset, limit: count }; + else if (this.dialect.rowNumberOverPaging && offset > 0) + select = this.getRowNumberOverSelect(select, offset, count); + else if (this.dialect.limitSelect) select.topRecords = count; const sql = treeToSql(this.driver, select, dumpSqlSelect); return sql; } diff --git a/packages/datalib/src/TableFormViewDisplay.ts b/packages/datalib/src/TableFormViewDisplay.ts index 3a3cac501..3335b39d3 100644 --- a/packages/datalib/src/TableFormViewDisplay.ts +++ b/packages/datalib/src/TableFormViewDisplay.ts @@ -29,9 +29,10 @@ export class TableFormViewDisplay extends FormViewDisplay { cache: GridCache, setCache: ChangeCacheFunc, dbinfo: DatabaseInfo, - displayOptions + displayOptions, + serverVersion ) { - super(config, setConfig, cache, setCache, driver, dbinfo); + super(config, setConfig, cache, setCache, driver, dbinfo, serverVersion); this.gridDisplay = new TableGridDisplay( tableName, driver, @@ -40,7 +41,8 @@ export class TableFormViewDisplay extends FormViewDisplay { cache, setCache, dbinfo, - displayOptions + displayOptions, + serverVersion ); this.gridDisplay.addAllExpandedColumnsToSelected = true; diff --git a/packages/datalib/src/TableGridDisplay.ts b/packages/datalib/src/TableGridDisplay.ts index f213fd8ed..16d215e42 100644 --- a/packages/datalib/src/TableGridDisplay.ts +++ b/packages/datalib/src/TableGridDisplay.ts @@ -18,9 +18,10 @@ export class TableGridDisplay extends GridDisplay { cache: GridCache, setCache: ChangeCacheFunc, dbinfo: DatabaseInfo, - public displayOptions: any + public displayOptions: any, + serverVersion ) { - super(config, setConfig, cache, setCache, driver, dbinfo); + super(config, setConfig, cache, setCache, driver, dbinfo, serverVersion); this.table = this.findTable(tableName); if (!this.table) { diff --git a/packages/datalib/src/ViewGridDisplay.ts b/packages/datalib/src/ViewGridDisplay.ts index 365af2e48..bc785426b 100644 --- a/packages/datalib/src/ViewGridDisplay.ts +++ b/packages/datalib/src/ViewGridDisplay.ts @@ -10,9 +10,10 @@ export class ViewGridDisplay extends GridDisplay { config: GridConfig, setConfig: ChangeConfigFunc, cache: GridCache, - setCache: ChangeCacheFunc + setCache: ChangeCacheFunc, + serverVersion ) { - super(config, setConfig, cache, setCache, driver); + super(config, setConfig, cache, setCache, driver, serverVersion); this.columns = this.getDisplayColumns(view); this.filterable = true; this.sortable = true; diff --git a/packages/sqltree/src/dumpSqlCondition.ts b/packages/sqltree/src/dumpSqlCondition.ts index 46bd8ef27..4ef5d02bf 100644 --- a/packages/sqltree/src/dumpSqlCondition.ts +++ b/packages/sqltree/src/dumpSqlCondition.ts @@ -62,5 +62,12 @@ export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) { dumpSqlSelect(dmp, condition.subQuery); dmp.put(')'); break; + case 'between': + dumpSqlExpression(dmp, condition.expr); + dmp.put(' ^between '); + dumpSqlExpression(dmp, condition.left); + dmp.put(' ^and '); + dumpSqlExpression(dmp, condition.right); + break; } } diff --git a/packages/sqltree/src/dumpSqlExpression.ts b/packages/sqltree/src/dumpSqlExpression.ts index 0aceeaa78..433bb740f 100644 --- a/packages/sqltree/src/dumpSqlExpression.ts +++ b/packages/sqltree/src/dumpSqlExpression.ts @@ -38,5 +38,14 @@ export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) { case 'transform': dmp.transform(expr.transform, () => dumpSqlExpression(dmp, expr.expr)); break; + + case 'rowNumber': + dmp.put(" ^row_number() ^over (^order ^by "); + dmp.putCollection(', ', expr.orderBy, x => { + dumpSqlExpression(dmp, x); + dmp.put(' %k', x.direction); + }); + dmp.put(")"); + break; } } diff --git a/packages/sqltree/src/types.ts b/packages/sqltree/src/types.ts index 5bcd09551..b9c58dda7 100644 --- a/packages/sqltree/src/types.ts +++ b/packages/sqltree/src/types.ts @@ -92,6 +92,13 @@ export interface NotExistsCondition { subQuery: Select; } +export interface BetweenCondition { + conditionType: 'between'; + expr: Expression; + left: Expression; + right: Expression; +} + export type Condition = | BinaryCondition | NotCondition @@ -99,7 +106,8 @@ export type Condition = | CompoudCondition | LikeCondition | ExistsCondition - | NotExistsCondition; + | NotExistsCondition + | BetweenCondition; export interface Source { name?: NamedObjectInfo; @@ -153,13 +161,19 @@ export interface TranformExpression { transform: TransformType; } +export interface RowNumberExpression { + exprType: 'rowNumber'; + orderBy: OrderByExpression[]; +} + export type Expression = | ColumnRefExpression | ValueExpression | PlaceholderExpression | RawExpression | CallExpression - | TranformExpression; + | TranformExpression + | RowNumberExpression; export type OrderByExpression = Expression & { direction: 'ASC' | 'DESC' }; export type ResultField = Expression & { alias?: string }; diff --git a/packages/types/dialect.d.ts b/packages/types/dialect.d.ts index 8cd6ecdbe..cacc14a53 100644 --- a/packages/types/dialect.d.ts +++ b/packages/types/dialect.d.ts @@ -1,6 +1,7 @@ export interface SqlDialect { rangeSelect?: boolean; limitSelect?: boolean; + rowNumberOverPaging?: boolean; stringEscapeChar: string; offsetFetchRangeSyntax?: boolean; quoteIdentifier(s: string): string; diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index 0862fce8d..26b0116a7 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -61,6 +61,7 @@ export interface EngineDriver { analyseFull(pool: any): Promise; analyseIncremental(pool: any, structure: DatabaseInfo): Promise; dialect: SqlDialect; + dialectByVersion(version): SqlDialect; createDumper(): SqlDumper; getAuthTypes(): EngineAuthType[]; readCollection(pool: any, options: ReadCollectionOptions): Promise; diff --git a/packages/web/src/datagrid/TableDataGrid.svelte b/packages/web/src/datagrid/TableDataGrid.svelte index 6025b5f93..af7da844b 100644 --- a/packages/web/src/datagrid/TableDataGrid.svelte +++ b/packages/web/src/datagrid/TableDataGrid.svelte @@ -43,7 +43,7 @@ $: connection = useConnectionInfo({ conid }); $: dbinfo = useDatabaseInfo({ conid, database }); - // $: serverVersion = useDatabaseServerVersion({ conid, database }); + $: serverVersion = useDatabaseServerVersion({ conid, database }); // $: console.log('serverVersion', $serverVersion); @@ -53,31 +53,35 @@ // $: console.log('display', display); - $: display = connection - ? new TableGridDisplay( - { schemaName, pureName }, - findEngineDriver($connection, $extensions), - config, - setConfig, - cache, - setCache, - $dbinfo, - { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) } - ) - : null; + $: display = + connection && $serverVersion + ? new TableGridDisplay( + { schemaName, pureName }, + findEngineDriver($connection, $extensions), + config, + setConfig, + cache, + setCache, + $dbinfo, + { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) }, + $serverVersion + ) + : null; - $: formDisplay = connection - ? new TableFormViewDisplay( - { schemaName, pureName }, - findEngineDriver($connection, $extensions), - config, - setConfig, - cache, - setCache, - $dbinfo, - { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) } - ) - : null; + $: formDisplay = + connection && $serverVersion + ? new TableFormViewDisplay( + { schemaName, pureName }, + findEngineDriver($connection, $extensions), + config, + setConfig, + cache, + setCache, + $dbinfo, + { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) }, + $serverVersion + ) + : null; const setChildConfig = (value, reference = undefined) => { if (_.isFunction(value)) { diff --git a/packages/web/src/tabs/ViewDataTab.svelte b/packages/web/src/tabs/ViewDataTab.svelte index be55c0095..8a1c8a46a 100644 --- a/packages/web/src/tabs/ViewDataTab.svelte +++ b/packages/web/src/tabs/ViewDataTab.svelte @@ -11,7 +11,7 @@ import DataGrid from '../datagrid/DataGrid.svelte'; import SqlDataGridCore from '../datagrid/SqlDataGridCore.svelte'; import { extensions } from '../stores'; - import { useConnectionInfo, useViewInfo } from '../utility/metadataLoaders'; + import { useConnectionInfo, useDatabaseServerVersion, useViewInfo } from '../utility/metadataLoaders'; import useGridConfig from '../utility/useGridConfig'; export let tabid; @@ -22,12 +22,13 @@ $: connection = useConnectionInfo({ conid }); $: viewInfo = useViewInfo({ conid, database, schemaName, pureName }); + $: serverVersion = useDatabaseServerVersion({ conid, database }); const config = useGridConfig(tabid); const cache = writable(createGridCache()); $: display = - $viewInfo && $connection + $viewInfo && $connection && $serverVersion ? new ViewGridDisplay( $viewInfo, findEngineDriver($connection, $extensions), @@ -35,7 +36,8 @@ $config, config.update, $cache, - cache.update + cache.update, + $serverVersion ) : null; diff --git a/plugins/dbgate-plugin-mssql/src/frontend/driver.js b/plugins/dbgate-plugin-mssql/src/frontend/driver.js index 0c20c5745..cbdf9d263 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/driver.js @@ -6,6 +6,7 @@ const dialect = { limitSelect: true, rangeSelect: true, offsetFetchRangeSyntax: true, + rowNumberOverPaging: true, stringEscapeChar: "'", fallbackDataType: 'nvarchar(max)', explicitDropConstraint: false, @@ -21,6 +22,16 @@ const driver = { ...driverBase, dumperClass: MsSqlDumper, dialect, + dialectByVersion(version) { + if (version && version.productVersionNumber < 11) { + return { + ...dialect, + rangeSelect: false, + offsetFetchRangeSyntax: false, + }; + } + return dialect; + }, engine: 'mssql@dbgate-plugin-mssql', title: 'Microsoft SQL Server', defaultPort: 1433,