select page by row_number for MS SQL 2008 #93

This commit is contained in:
Jan Prochazka
2021-04-25 11:48:23 +02:00
parent 8ff706a17f
commit 67e1913683
13 changed files with 178 additions and 45 deletions

View File

@@ -1,6 +1,14 @@
import _ from 'lodash'; import _ from 'lodash';
import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc } from './GridConfig'; 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 { parseFilter, getFilterType, getFilterValueExpression } from 'dbgate-filterparser';
import { filterName } from './filterName'; import { filterName } from './filterName';
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet'; import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
@@ -12,6 +20,7 @@ export class FormViewDisplay {
isLoadedCorrectly = true; isLoadedCorrectly = true;
columns: DisplayColumn[]; columns: DisplayColumn[];
public baseTable: TableInfo; public baseTable: TableInfo;
dialect: SqlDialect;
constructor( constructor(
public config: GridConfig, public config: GridConfig,
@@ -19,8 +28,11 @@ export class FormViewDisplay {
public cache: GridCache, public cache: GridCache,
protected setCache: ChangeCacheFunc, protected setCache: ChangeCacheFunc,
public driver?: EngineDriver, 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) { addFilterColumn(column) {
if (!column) return; if (!column) return;

View File

@@ -8,6 +8,7 @@ import {
NamedObjectInfo, NamedObjectInfo,
DatabaseInfo, DatabaseInfo,
CollectionInfo, CollectionInfo,
SqlDialect,
} from 'dbgate-types'; } from 'dbgate-types';
import { parseFilter, getFilterType } from 'dbgate-filterparser'; import { parseFilter, getFilterType } from 'dbgate-filterparser';
import { filterName } from './filterName'; import { filterName } from './filterName';
@@ -60,8 +61,12 @@ export abstract class GridDisplay {
public cache: GridCache, public cache: GridCache,
protected setCache: ChangeCacheFunc, protected setCache: ChangeCacheFunc,
public driver?: EngineDriver, 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[]; columns: DisplayColumn[];
baseTable?: TableInfo; baseTable?: TableInfo;
baseCollection?: CollectionInfo; baseCollection?: CollectionInfo;
@@ -460,12 +465,75 @@ export abstract class GridDisplay {
return select; 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) { getPageQuery(offset: number, count: number) {
if (!this.driver) return null; if (!this.driver) return null;
const select = this.createSelect(); let select = this.createSelect();
if (!select) return null; if (!select) return null;
if (this.driver.dialect.rangeSelect) select.range = { offset: offset, limit: count }; if (this.dialect.rangeSelect) select.range = { offset: offset, limit: count };
else if (this.driver.dialect.limitSelect) select.topRecords = 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); const sql = treeToSql(this.driver, select, dumpSqlSelect);
return sql; return sql;
} }

View File

@@ -29,9 +29,10 @@ export class TableFormViewDisplay extends FormViewDisplay {
cache: GridCache, cache: GridCache,
setCache: ChangeCacheFunc, setCache: ChangeCacheFunc,
dbinfo: DatabaseInfo, dbinfo: DatabaseInfo,
displayOptions displayOptions,
serverVersion
) { ) {
super(config, setConfig, cache, setCache, driver, dbinfo); super(config, setConfig, cache, setCache, driver, dbinfo, serverVersion);
this.gridDisplay = new TableGridDisplay( this.gridDisplay = new TableGridDisplay(
tableName, tableName,
driver, driver,
@@ -40,7 +41,8 @@ export class TableFormViewDisplay extends FormViewDisplay {
cache, cache,
setCache, setCache,
dbinfo, dbinfo,
displayOptions displayOptions,
serverVersion
); );
this.gridDisplay.addAllExpandedColumnsToSelected = true; this.gridDisplay.addAllExpandedColumnsToSelected = true;

View File

@@ -18,9 +18,10 @@ export class TableGridDisplay extends GridDisplay {
cache: GridCache, cache: GridCache,
setCache: ChangeCacheFunc, setCache: ChangeCacheFunc,
dbinfo: DatabaseInfo, 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); this.table = this.findTable(tableName);
if (!this.table) { if (!this.table) {

View File

@@ -10,9 +10,10 @@ export class ViewGridDisplay extends GridDisplay {
config: GridConfig, config: GridConfig,
setConfig: ChangeConfigFunc, setConfig: ChangeConfigFunc,
cache: GridCache, 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.columns = this.getDisplayColumns(view);
this.filterable = true; this.filterable = true;
this.sortable = true; this.sortable = true;

View File

@@ -62,5 +62,12 @@ export function dumpSqlCondition(dmp: SqlDumper, condition: Condition) {
dumpSqlSelect(dmp, condition.subQuery); dumpSqlSelect(dmp, condition.subQuery);
dmp.put(')'); dmp.put(')');
break; break;
case 'between':
dumpSqlExpression(dmp, condition.expr);
dmp.put(' ^between ');
dumpSqlExpression(dmp, condition.left);
dmp.put(' ^and ');
dumpSqlExpression(dmp, condition.right);
break;
} }
} }

View File

@@ -38,5 +38,14 @@ export function dumpSqlExpression(dmp: SqlDumper, expr: Expression) {
case 'transform': case 'transform':
dmp.transform(expr.transform, () => dumpSqlExpression(dmp, expr.expr)); dmp.transform(expr.transform, () => dumpSqlExpression(dmp, expr.expr));
break; 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;
} }
} }

View File

@@ -92,6 +92,13 @@ export interface NotExistsCondition {
subQuery: Select; subQuery: Select;
} }
export interface BetweenCondition {
conditionType: 'between';
expr: Expression;
left: Expression;
right: Expression;
}
export type Condition = export type Condition =
| BinaryCondition | BinaryCondition
| NotCondition | NotCondition
@@ -99,7 +106,8 @@ export type Condition =
| CompoudCondition | CompoudCondition
| LikeCondition | LikeCondition
| ExistsCondition | ExistsCondition
| NotExistsCondition; | NotExistsCondition
| BetweenCondition;
export interface Source { export interface Source {
name?: NamedObjectInfo; name?: NamedObjectInfo;
@@ -153,13 +161,19 @@ export interface TranformExpression {
transform: TransformType; transform: TransformType;
} }
export interface RowNumberExpression {
exprType: 'rowNumber';
orderBy: OrderByExpression[];
}
export type Expression = export type Expression =
| ColumnRefExpression | ColumnRefExpression
| ValueExpression | ValueExpression
| PlaceholderExpression | PlaceholderExpression
| RawExpression | RawExpression
| CallExpression | CallExpression
| TranformExpression; | TranformExpression
| RowNumberExpression;
export type OrderByExpression = Expression & { direction: 'ASC' | 'DESC' }; export type OrderByExpression = Expression & { direction: 'ASC' | 'DESC' };
export type ResultField = Expression & { alias?: string }; export type ResultField = Expression & { alias?: string };

View File

@@ -1,6 +1,7 @@
export interface SqlDialect { export interface SqlDialect {
rangeSelect?: boolean; rangeSelect?: boolean;
limitSelect?: boolean; limitSelect?: boolean;
rowNumberOverPaging?: boolean;
stringEscapeChar: string; stringEscapeChar: string;
offsetFetchRangeSyntax?: boolean; offsetFetchRangeSyntax?: boolean;
quoteIdentifier(s: string): string; quoteIdentifier(s: string): string;

View File

@@ -61,6 +61,7 @@ export interface EngineDriver {
analyseFull(pool: any): Promise<DatabaseInfo>; analyseFull(pool: any): Promise<DatabaseInfo>;
analyseIncremental(pool: any, structure: DatabaseInfo): Promise<DatabaseInfo>; analyseIncremental(pool: any, structure: DatabaseInfo): Promise<DatabaseInfo>;
dialect: SqlDialect; dialect: SqlDialect;
dialectByVersion(version): SqlDialect;
createDumper(): SqlDumper; createDumper(): SqlDumper;
getAuthTypes(): EngineAuthType[]; getAuthTypes(): EngineAuthType[];
readCollection(pool: any, options: ReadCollectionOptions): Promise<any>; readCollection(pool: any, options: ReadCollectionOptions): Promise<any>;

View File

@@ -43,7 +43,7 @@
$: connection = useConnectionInfo({ conid }); $: connection = useConnectionInfo({ conid });
$: dbinfo = useDatabaseInfo({ conid, database }); $: dbinfo = useDatabaseInfo({ conid, database });
// $: serverVersion = useDatabaseServerVersion({ conid, database }); $: serverVersion = useDatabaseServerVersion({ conid, database });
// $: console.log('serverVersion', $serverVersion); // $: console.log('serverVersion', $serverVersion);
@@ -53,31 +53,35 @@
// $: console.log('display', display); // $: console.log('display', display);
$: display = connection $: display =
? new TableGridDisplay( connection && $serverVersion
{ schemaName, pureName }, ? new TableGridDisplay(
findEngineDriver($connection, $extensions), { schemaName, pureName },
config, findEngineDriver($connection, $extensions),
setConfig, config,
cache, setConfig,
setCache, cache,
$dbinfo, setCache,
{ showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) } $dbinfo,
) { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) },
: null; $serverVersion
)
: null;
$: formDisplay = connection $: formDisplay =
? new TableFormViewDisplay( connection && $serverVersion
{ schemaName, pureName }, ? new TableFormViewDisplay(
findEngineDriver($connection, $extensions), { schemaName, pureName },
config, findEngineDriver($connection, $extensions),
setConfig, config,
cache, setConfig,
setCache, cache,
$dbinfo, setCache,
{ showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) } $dbinfo,
) { showHintColumns: getBoolSettingsValue('dataGrid.showHintColumns', true) },
: null; $serverVersion
)
: null;
const setChildConfig = (value, reference = undefined) => { const setChildConfig = (value, reference = undefined) => {
if (_.isFunction(value)) { if (_.isFunction(value)) {

View File

@@ -11,7 +11,7 @@
import DataGrid from '../datagrid/DataGrid.svelte'; import DataGrid from '../datagrid/DataGrid.svelte';
import SqlDataGridCore from '../datagrid/SqlDataGridCore.svelte'; import SqlDataGridCore from '../datagrid/SqlDataGridCore.svelte';
import { extensions } from '../stores'; import { extensions } from '../stores';
import { useConnectionInfo, useViewInfo } from '../utility/metadataLoaders'; import { useConnectionInfo, useDatabaseServerVersion, useViewInfo } from '../utility/metadataLoaders';
import useGridConfig from '../utility/useGridConfig'; import useGridConfig from '../utility/useGridConfig';
export let tabid; export let tabid;
@@ -22,12 +22,13 @@
$: connection = useConnectionInfo({ conid }); $: connection = useConnectionInfo({ conid });
$: viewInfo = useViewInfo({ conid, database, schemaName, pureName }); $: viewInfo = useViewInfo({ conid, database, schemaName, pureName });
$: serverVersion = useDatabaseServerVersion({ conid, database });
const config = useGridConfig(tabid); const config = useGridConfig(tabid);
const cache = writable(createGridCache()); const cache = writable(createGridCache());
$: display = $: display =
$viewInfo && $connection $viewInfo && $connection && $serverVersion
? new ViewGridDisplay( ? new ViewGridDisplay(
$viewInfo, $viewInfo,
findEngineDriver($connection, $extensions), findEngineDriver($connection, $extensions),
@@ -35,7 +36,8 @@
$config, $config,
config.update, config.update,
$cache, $cache,
cache.update cache.update,
$serverVersion
) )
: null; : null;
</script> </script>

View File

@@ -6,6 +6,7 @@ const dialect = {
limitSelect: true, limitSelect: true,
rangeSelect: true, rangeSelect: true,
offsetFetchRangeSyntax: true, offsetFetchRangeSyntax: true,
rowNumberOverPaging: true,
stringEscapeChar: "'", stringEscapeChar: "'",
fallbackDataType: 'nvarchar(max)', fallbackDataType: 'nvarchar(max)',
explicitDropConstraint: false, explicitDropConstraint: false,
@@ -21,6 +22,16 @@ const driver = {
...driverBase, ...driverBase,
dumperClass: MsSqlDumper, dumperClass: MsSqlDumper,
dialect, dialect,
dialectByVersion(version) {
if (version && version.productVersionNumber < 11) {
return {
...dialect,
rangeSelect: false,
offsetFetchRangeSyntax: false,
};
}
return dialect;
},
engine: 'mssql@dbgate-plugin-mssql', engine: 'mssql@dbgate-plugin-mssql',
title: 'Microsoft SQL Server', title: 'Microsoft SQL Server',
defaultPort: 1433, defaultPort: 1433,