mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat(datasource): Support oracle
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
"""006_add_ds_field
|
||||
|
||||
Revision ID: e6276ddab06e
|
||||
Revises: 0a6f11be9be4
|
||||
Create Date: 2025-05-22 11:43:40.176878
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlmodel.sql.sqltypes
|
||||
from sqlalchemy.dialects import postgresql
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e6276ddab06e'
|
||||
down_revision = '0a6f11be9be4'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('core_datasource', sa.Column('type_name', sqlmodel.sql.sqltypes.AutoString(length=64), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('core_datasource', 'type_name')
|
||||
# ### end Alembic commands ###
|
||||
@@ -15,6 +15,7 @@ from ..crud.field import delete_field_by_ds_id, update_field
|
||||
from ..crud.table import delete_table_by_ds_id, update_table
|
||||
from ..models.datasource import CoreDatasource, CreateDatasource, CoreTable, CoreField, ColumnSchema, EditObj, \
|
||||
DatasourceConf
|
||||
from apps.db.type import db_type_relation
|
||||
|
||||
|
||||
def get_datasource_list(session: SessionDep):
|
||||
@@ -41,6 +42,7 @@ def create_ds(session: SessionDep, user: CurrentUser, create_ds: CreateDatasourc
|
||||
# status = check_status(session, ds)
|
||||
ds.create_by = user.id
|
||||
ds.status = "Success"
|
||||
ds.type_name = db_type_relation()[ds.type]
|
||||
record = CoreDatasource(**ds.model_dump())
|
||||
session.add(record)
|
||||
session.flush()
|
||||
@@ -202,4 +204,8 @@ def preview(session: SessionDep, id: int, data: EditObj):
|
||||
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY"""
|
||||
elif ds.type == "pg" or ds.type == "excel":
|
||||
sql = f"""SELECT "{'", "'.join([f.field_name for f in data.fields if f.checked])}" FROM "{conf.dbSchema}"."{data.table.table_name}" LIMIT 100"""
|
||||
elif ds.type == "oracle":
|
||||
sql = f"""SELECT "{'", "'.join([f.field_name for f in data.fields if f.checked])}" FROM "{conf.dbSchema}"."{data.table.table_name}"
|
||||
ORDER BY "{data.fields[0].field_name}"
|
||||
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY"""
|
||||
return exec_sql(ds, sql)
|
||||
|
||||
@@ -12,6 +12,7 @@ class CoreDatasource(SQLModel, table=True):
|
||||
name: str = Field(max_length=128, nullable=False)
|
||||
description: str = Field(max_length=512, nullable=True)
|
||||
type: str = Field(max_length=64)
|
||||
type_name: str = Field(max_length=64, nullable=True)
|
||||
configuration: str = Field(sa_column=Column(Text))
|
||||
create_time: datetime = Field(sa_column=Column(DateTime(timezone=True), nullable=True))
|
||||
create_by: int = Field(sa_column=Column(BigInteger()))
|
||||
@@ -71,6 +72,7 @@ class DatasourceConf(BaseModel):
|
||||
dbSchema: str = ''
|
||||
filename: str = ''
|
||||
sheets: List = ''
|
||||
mode: str = ''
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
@@ -83,7 +85,8 @@ class DatasourceConf(BaseModel):
|
||||
"extraJdbc": self.extraJdbc,
|
||||
"dbSchema": self.dbSchema,
|
||||
"filename": self.filename,
|
||||
"sheets": self.sheets
|
||||
"sheets": self.sheets,
|
||||
"mode": self.mode
|
||||
}
|
||||
|
||||
|
||||
|
||||
+65
-9
@@ -16,19 +16,30 @@ def get_uri(ds: CoreDatasource):
|
||||
db_url: str
|
||||
if ds.type == "mysql":
|
||||
if conf.extraJdbc is not None and conf.extraJdbc != '':
|
||||
db_url = f"mysql+pymysql://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}?{urllib.parse.quote(conf.extraJdbc)}"
|
||||
db_url = f"mysql+pymysql://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}?{conf.extraJdbc}"
|
||||
else:
|
||||
db_url = f"mysql+pymysql://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}"
|
||||
elif ds.type == "sqlServer":
|
||||
if conf.extraJdbc is not None and conf.extraJdbc != '':
|
||||
db_url = f"mssql+pymssql://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}?{urllib.parse.quote(conf.extraJdbc)}"
|
||||
db_url = f"mssql+pymssql://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}?{conf.extraJdbc}"
|
||||
else:
|
||||
db_url = f"mssql+pymssql://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}"
|
||||
elif ds.type == "pg" or ds.type == "excel":
|
||||
if conf.extraJdbc is not None and conf.extraJdbc != '':
|
||||
db_url = f"postgresql+psycopg2://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}?{urllib.parse.quote(conf.extraJdbc)}"
|
||||
db_url = f"postgresql+psycopg2://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}?{conf.extraJdbc}"
|
||||
else:
|
||||
db_url = f"postgresql+psycopg2://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}"
|
||||
elif ds.type == "oracle":
|
||||
if conf.mode == "service_name":
|
||||
if conf.extraJdbc is not None and conf.extraJdbc != '':
|
||||
db_url = f"oracle+oracledb://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}?service_name={urllib.parse.quote(conf.database)}&{conf.extraJdbc}"
|
||||
else:
|
||||
db_url = f"oracle+oracledb://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}?service_name={urllib.parse.quote(conf.database)}"
|
||||
else:
|
||||
if conf.extraJdbc is not None and conf.extraJdbc != '':
|
||||
db_url = f"oracle+oracledb://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}?{conf.extraJdbc}"
|
||||
else:
|
||||
db_url = f"oracle+oracledb://{urllib.parse.quote(conf.username)}:{urllib.parse.quote(conf.password)}@{conf.host}:{conf.port}/{urllib.parse.quote(conf.database)}"
|
||||
else:
|
||||
raise 'The datasource type not support.'
|
||||
return db_url
|
||||
@@ -65,7 +76,7 @@ def get_tables(ds: CoreDatasource):
|
||||
FROM
|
||||
information_schema.TABLES
|
||||
WHERE
|
||||
TABLE_SCHEMA = '{conf.database}';
|
||||
TABLE_SCHEMA = '{conf.database}'
|
||||
"""
|
||||
elif ds.type == "sqlServer":
|
||||
sql = f"""
|
||||
@@ -81,7 +92,7 @@ def get_tables(ds: CoreDatasource):
|
||||
AND ep.name = 'MS_Description'
|
||||
WHERE
|
||||
t.TABLE_TYPE IN ('BASE TABLE', 'VIEW')
|
||||
AND t.TABLE_SCHEMA = '{conf.dbSchema}';
|
||||
AND t.TABLE_SCHEMA = '{conf.dbSchema}'
|
||||
"""
|
||||
elif ds.type == "pg" or ds.type == "excel":
|
||||
sql = """
|
||||
@@ -99,7 +110,27 @@ def get_tables(ds: CoreDatasource):
|
||||
AND c.relkind IN ('r', 'v')
|
||||
AND c.relname NOT LIKE 'pg_%'
|
||||
AND c.relname NOT LIKE 'sql_%'
|
||||
ORDER BY c.relname;
|
||||
ORDER BY c.relname
|
||||
"""
|
||||
elif ds.type == "oracle":
|
||||
sql = f"""
|
||||
SELECT
|
||||
t.TABLE_NAME AS "TABLE_NAME",
|
||||
NVL(c.COMMENTS, '') AS "TABLE_COMMENT"
|
||||
FROM (
|
||||
SELECT TABLE_NAME, 'TABLE' AS OBJECT_TYPE
|
||||
FROM DBA_TABLES
|
||||
WHERE OWNER = '{conf.dbSchema}'
|
||||
UNION ALL
|
||||
SELECT VIEW_NAME AS TABLE_NAME, 'VIEW' AS OBJECT_TYPE
|
||||
FROM DBA_VIEWS
|
||||
WHERE OWNER = '{conf.dbSchema}'
|
||||
) t
|
||||
LEFT JOIN DBA_TAB_COMMENTS c
|
||||
ON t.TABLE_NAME = c.TABLE_NAME
|
||||
AND c.TABLE_TYPE = t.OBJECT_TYPE
|
||||
AND c.OWNER = '{conf.dbSchema}'
|
||||
ORDER BY t.TABLE_NAME
|
||||
"""
|
||||
|
||||
result = session.execute(text(sql))
|
||||
@@ -130,7 +161,7 @@ def get_fields(ds: CoreDatasource, table_name: str = None):
|
||||
WHERE
|
||||
TABLE_SCHEMA = '{conf.database}'
|
||||
"""
|
||||
sql2 = f" AND TABLE_NAME = '{table_name}';" if table_name is not None and table_name != "" else ";"
|
||||
sql2 = f" AND TABLE_NAME = '{table_name}'" if table_name is not None and table_name != "" else ""
|
||||
sql = sql1 + sql2
|
||||
elif ds.type == "sqlServer":
|
||||
sql1 = f"""
|
||||
@@ -148,7 +179,7 @@ def get_fields(ds: CoreDatasource, table_name: str = None):
|
||||
WHERE
|
||||
C.TABLE_SCHEMA = '{conf.dbSchema}'
|
||||
"""
|
||||
sql2 = f" AND C.TABLE_NAME = '{table_name}';" if table_name is not None and table_name != "" else ";"
|
||||
sql2 = f" AND C.TABLE_NAME = '{table_name}'" if table_name is not None and table_name != "" else ""
|
||||
sql = sql1 + sql2
|
||||
elif ds.type == "pg" or ds.type == "excel":
|
||||
sql1 = """
|
||||
@@ -167,7 +198,32 @@ def get_fields(ds: CoreDatasource, table_name: str = None):
|
||||
AND a.attnum > 0
|
||||
AND NOT a.attisdropped
|
||||
"""
|
||||
sql2 = f" AND c.relname = '{table_name}';" if table_name is not None and table_name != "" else ";"
|
||||
sql2 = f" AND c.relname = '{table_name}'" if table_name is not None and table_name != "" else ""
|
||||
sql = sql1 + sql2
|
||||
elif ds.type == "oracle":
|
||||
sql1 = f"""
|
||||
SELECT
|
||||
col.COLUMN_NAME AS "COLUMN_NAME",
|
||||
(CASE
|
||||
WHEN col.DATA_TYPE IN ('VARCHAR2', 'CHAR', 'NVARCHAR2', 'NCHAR')
|
||||
THEN col.DATA_TYPE || '(' || col.DATA_LENGTH || ')'
|
||||
WHEN col.DATA_TYPE = 'NUMBER' AND col.DATA_PRECISION IS NOT NULL
|
||||
THEN col.DATA_TYPE || '(' || col.DATA_PRECISION ||
|
||||
CASE WHEN col.DATA_SCALE > 0 THEN ',' || col.DATA_SCALE END || ')'
|
||||
ELSE col.DATA_TYPE
|
||||
END) AS "DATA_TYPE",
|
||||
NVL(com.COMMENTS, '') AS "COLUMN_COMMENT"
|
||||
FROM
|
||||
DBA_TAB_COLUMNS col
|
||||
LEFT JOIN
|
||||
DBA_COL_COMMENTS com
|
||||
ON col.OWNER = com.OWNER
|
||||
AND col.TABLE_NAME = com.TABLE_NAME
|
||||
AND col.COLUMN_NAME = com.COLUMN_NAME
|
||||
WHERE
|
||||
col.OWNER = '{conf.dbSchema}'
|
||||
"""
|
||||
sql2 = f" AND col.TABLE_NAME = '{table_name}'" if table_name is not None and table_name != "" else ""
|
||||
sql = sql1 + sql2
|
||||
|
||||
result = session.execute(text(sql))
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# Author: Junjun
|
||||
# Date: 2025/5/22
|
||||
from typing import Dict
|
||||
|
||||
|
||||
def db_type_relation() -> Dict:
|
||||
return {
|
||||
"mysql": "MySQL",
|
||||
"sqlServer": "Microsoft SQL Server",
|
||||
"pg": "PostgreSQL",
|
||||
"excel": "Excel/CSV",
|
||||
"oracle": "Oracle"
|
||||
}
|
||||
@@ -32,7 +32,8 @@ dependencies = [
|
||||
"pymssql (>=2.3.4,<3.0.0)",
|
||||
"pandas (>=2.2.3,<3.0.0)",
|
||||
"openpyxl (>=3.1.5,<4.0.0)",
|
||||
"psycopg2 (>=2.9.10,<3.0.0)"
|
||||
"psycopg2 (>=2.9.10,<3.0.0)",
|
||||
"oracledb (>=3.1.1,<4.0.0)"
|
||||
]
|
||||
[[tool.uv.index]]
|
||||
url = "https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
|
||||
@@ -64,6 +64,12 @@
|
||||
<el-form-item label="Database">
|
||||
<el-input v-model="config.database" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Connect Mode" v-if="form.type === 'oracle'">
|
||||
<el-radio-group v-model="config.mode">
|
||||
<el-radio value="service_name">Service Name</el-radio>
|
||||
<el-radio value="sid">SID</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="Extra JDBC String">
|
||||
<el-input v-model="config.extraJdbc" />
|
||||
</el-form-item>
|
||||
@@ -71,6 +77,8 @@
|
||||
<el-input v-model="config.dbSchema" />
|
||||
<el-button link type="primary" :icon="Plus" v-if="false">Get Schema</el-button>
|
||||
</el-form-item>
|
||||
<span v-if="form.type === 'sqlServer'">Supported version: 2012+</span>
|
||||
<span v-else-if="form.type === 'oracle'">Supported version: 12+</span>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -143,7 +151,8 @@ const config = ref<any>({
|
||||
extraJdbc:'',
|
||||
dbSchema:'',
|
||||
filename:'',
|
||||
sheets: []
|
||||
sheets: [],
|
||||
mode:'service_name'
|
||||
})
|
||||
|
||||
const close = () => {
|
||||
@@ -176,6 +185,7 @@ const open = (item: any, editTable: boolean = false) => {
|
||||
config.value.dbSchema = configuration.dbSchema
|
||||
config.value.filename = configuration.filename
|
||||
config.value.sheets = configuration.sheets
|
||||
config.value.mode = configuration.mode
|
||||
}
|
||||
|
||||
if (editTable) {
|
||||
@@ -217,7 +227,8 @@ const open = (item: any, editTable: boolean = false) => {
|
||||
extraJdbc:'',
|
||||
dbSchema:'',
|
||||
filename:'',
|
||||
sheets: []
|
||||
sheets: [],
|
||||
mode:'service_name'
|
||||
}
|
||||
}
|
||||
dialogVisible.value = true
|
||||
@@ -268,7 +279,8 @@ const buildConf = () => {
|
||||
extraJdbc:config.value.extraJdbc,
|
||||
dbSchema:config.value.dbSchema,
|
||||
filename:config.value.filename,
|
||||
sheets:config.value.sheets
|
||||
sheets:config.value.sheets,
|
||||
mode:config.value.mode
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,12 @@
|
||||
<SQLServerDs v-else-if="ds.type === 'sqlServer'"/>
|
||||
<PgDs v-else-if="ds.type === 'pg'"/>
|
||||
<ExcelDs v-else-if="ds.type === 'excel'"/>
|
||||
<OracleDs v-else-if="ds.type === 'oracle'"/>
|
||||
</Icon>
|
||||
</div>
|
||||
<div class="connection-details">
|
||||
<div class="connection-name">{{ ds.name }}</div>
|
||||
<div class="connection-type">{{ dsRelation[ds.type] }}</div>
|
||||
<div class="connection-type">{{ ds.type_name }}</div>
|
||||
<div class="connection-host">{{ ds.description }}</div>
|
||||
<div class="connection-last">{{ datetimeFormat(ds.create_time) }}</div>
|
||||
</div>
|
||||
@@ -58,6 +59,7 @@ import MysqlDs from '@/assets/svg/ds/mysql-ds.svg'
|
||||
import SQLServerDs from '@/assets/svg/ds/sqlServer-ds.svg'
|
||||
import PgDs from '@/assets/svg/ds/pg-ds.svg'
|
||||
import ExcelDs from '@/assets/svg/ds/Excel-ds.svg'
|
||||
import OracleDs from '@/assets/svg/ds/oracle-ds.svg'
|
||||
import { Search, List, CreditCard } from '@element-plus/icons-vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import DsForm from './form.vue'
|
||||
@@ -65,7 +67,6 @@ import { datasourceApi } from '@/api/datasource'
|
||||
import { datetimeFormat } from '@/utils/utils'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { dsRelation } from '@/views/ds/js/ds-type'
|
||||
|
||||
const searchValue = ref<string>('')
|
||||
const dsForm = ref()
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
export const dsType = [
|
||||
{label:"MySQL", value:"mysql"},
|
||||
{label:"Microsoft SQL Server", value:"sqlServer"},
|
||||
{label:"PostgreSQL", value:"pg"},
|
||||
{label:"Microsoft SQL Server", value:"sqlServer"},
|
||||
{label:"Oracle", value:"oracle"},
|
||||
{label:"Excel/CSV", value:"excel"},
|
||||
]
|
||||
|
||||
export const haveSchema = [
|
||||
'sqlServer',
|
||||
'pg'
|
||||
]
|
||||
|
||||
export const dsRelation: any = {
|
||||
"mysql": "MySQL",
|
||||
"sqlServer": "Microsoft SQL Server",
|
||||
"pg": "PostgreSQL",
|
||||
"excel": "Excel/CSV",
|
||||
}
|
||||
'pg',
|
||||
'oracle'
|
||||
]
|
||||
Reference in New Issue
Block a user