cube.js 对于sql 方言支持的处理
sql 方言主要是为了进行不通数据库sql 兼容的支持,一般我们是不需要做的,但是一些特殊情况下我们
可能需要自己开发sql 方言处理
参考方法
server 初始化
packages/cubejs-server-core/src/core/server.ts
const options: ServerCoreInitializedOptions = {
dbType,
externalDbType,
devServer,
driverFactory: () => typeof dbType === 'string' && CubejsServerCore.createDriver(dbType),
dialectFactory: (ctx) => CubejsServerCore.lookupDriverClass(ctx.dbType).dialectClass &&
CubejsServerCore.lookupDriverClass(ctx.dbType).dialectClass(),
externalDriverFactory: externalDbType && (
() => new (CubejsServerCore.lookupDriverClass(externalDbType))({
host: process.env.CUBEJS_EXT_DB_HOST,
database: process.env.CUBEJS_EXT_DB_NAME,
port: process.env.CUBEJS_EXT_DB_PORT,
user: process.env.CUBEJS_EXT_DB_USER,
password: process.env.CUBEJS_EXT_DB_PASS,
})
),
externalDialectFactory: () => typeof externalDbType === 'string' &&
CubejsServerCore.lookupDriverClass(externalDbType).dialectClass &&
CubejsServerCore.lookupDriverClass(externalDbType).dialectClass(),
apiSecret: process.env.CUBEJS_API_SECRET,
telemetry: process.env.CUBEJS_TELEMETRY !== 'false',
scheduledRefreshTimeZones: process.env.CUBEJS_SCHEDULED_REFRESH_TIMEZONES &&
process.env.CUBEJS_SCHEDULED_REFRESH_TIMEZONES.split(',').map(t => t.trim()),
scheduledRefreshContexts: async () => [null],
basePath: '/cubejs-api',
dashboardAppPath: 'dashboard-app',
dashboardAppPort: 3000,
scheduledRefreshConcurrency: parseInt(process.env.CUBEJS_SCHEDULED_REFRESH_CONCURRENCY, 10),
preAggregationsSchema: getEnv('preAggregationsSchema') || (
devServer ? 'dev_pre_aggregations' : 'prod_pre_aggregations'
),
schemaPath: process.env.CUBEJS_SCHEMA_PATH || 'schema',
logger: opts.logger || process.env.NODE_ENV !== 'production'
? devLogger(process.env.CUBEJS_LOG_LEVEL)
: prodLogger(process.env.CUBEJS_LOG_LEVEL),
};
if (opts.contextToAppId && !opts.scheduledRefreshContexts) {
options.logger('Multitenancy Without ScheduledRefreshContexts', {
warning: (
'You are using multitenancy without configuring scheduledRefreshContexts, which can lead to issues where the ' +
'security context will be undefined while Cube.js will do background refreshing: ' +
'https://cube.dev/docs/config#options-reference-scheduled-refresh-contexts'
),
});
}
编译api 对于方言的处理
packages/cubejs-server-core/src/core/server.ts
public getCompilerApi(context: RequestContext) {
const appId = this.contextToAppId(context);
let compilerApi = this.compilerCache.get(appId);
const currentSchemaVersion = this.options.schemaVersion && (() => this.options.schemaVersion(context));
if (!compilerApi) {
compilerApi = this.createCompilerApi(
this.repositoryFactory(context), {
dbType: (dataSourceContext) => this.contextToDbType({
externalDbType: this.contextToExternalDbType(context),
dialectClass: (dialectContext) => this.options.dialectFactory &&
this.options.dialectFactory({
externalDialectClass: this.options.externalDialectFactory && this.options.externalDialectFactory(context),
schemaVersion: currentSchemaVersion,
preAggregationsSchema: this.preAggregationsSchema(context),
context,
allowJsDuplicatePropsInSchema: this.options.allowJsDuplicatePropsInSchema
}
);
this.compilerCache.set(appId, compilerApi);
}
compilerApi.schemaVersion = currentSchemaVersion;
return compilerApi;
}
方法处理部分(主要是进行了一个判断,没有的使用默认的)
packages/cubejs-schema-compiler/src/adapter/QueryBuilder.js
export const createQuery = (compilers, dbType, queryOptions) => {
if (!queryOptions.dialectClass && !ADAPTERS[dbType]) {
return null;
}
let externalQueryClass = queryOptions.externalDialectClass;
if (!externalQueryClass && queryOptions.externalDbType) {
if (!ADAPTERS[queryOptions.externalDbType]) {
throw new Error(`Dialect for '${queryOptions.externalDbType}' is not found`);
}
externalQueryClass = ADAPTERS[queryOptions.externalDbType];
}
// 有使用方言,没有的使用默认的driver 基于BaseQuery的实现
return new (queryOptions.dialectClass || ADAPTERS[dbType])(compilers, {
externalQueryClass
});
};
默认提供的adapter
packages/cubejs-schema-compiler/src/adapter/QueryBuilder.js
const ADAPTERS = {
postgres: PostgresQuery,
redshift: RedshiftQuery,
mysql: MysqlQuery,
mysqlauroraserverless: MysqlQuery,
mongobi: MongoBiQuery,
mssql: MssqlQuery,
bigquery: BigqueryQuery,
prestodb: PrestodbQuery,
qubole_prestodb: PrestodbQuery,
athena: PrestodbQuery,
vertica: VerticaQuery,
snowflake: SnowflakeQuery,
clickhouse: ClickHouseQuery,
hive: HiveQuery,
oracle: OracleQuery,
sqlite: SqliteQuery,
awselasticsearch: AWSElasticSearchQuery,
elasticsearch: ElasticSearchQuery
};
参考方言的编写
可以参考druid,参考格式
packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
packages/cubejs-query-orchestrator/src/driver/BaseDriver.js
packages/cubejs-druid-driver/src/DruidDriver.ts
export class DruidDriver extends BaseDriver {
protected readonly config: DruidDriverConfiguration;
protected readonly client: DruidClient;
// 此处比较重要
public static dialectClass() {
return DruidQuery;
}
DruidQuery 的实现继承自BaseQuery
说明
官方提供方言支持的核心也是让大家方便的进行driver的开发,因为一些内置的driver 是直接打包在
packages/cubejs-schema-compiler中了,修改此包是不明智的选择,同时也不利于社区参与贡献
如果关注代码会发现有一个直接使用基于npm 包模式的驱动依赖管理,处理参考代码
public static driverDependencies(dbType: DatabaseType) {
if (DriverDependencies[dbType]) {
return DriverDependencies[dbType];
} else if (fs.existsSync(path.join('node_modules', `${dbType}-cubejs-driver`))) {
return `${dbType}-cubejs-driver`;
}
throw new Error(`Unsupported db type: ${dbType}`);
}
所以只要我们的格式符合${dbType}-cubejs-driver
并且是通过npm 包发布的,也就是一个合格的driver
参考资料
https://github.com/cube-js/cube.js/blob/master/CONTRIBUTING.md#implementing-sql-dialect