|
1 | 1 | package main |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "strings" |
| 5 | + |
| 6 | + "github.com/BemiHQ/BemiDB/src/common" |
4 | 7 | pgQuery "github.com/pganalyze/pg_query_go/v6" |
5 | 8 | ) |
6 | 9 |
|
@@ -42,35 +45,60 @@ func (parser *ParserTable) IsTableFromInformationSchema(qSchemaTable QuerySchema |
42 | 45 | return qSchemaTable.Schema == PG_SCHEMA_INFORMATION_SCHEMA |
43 | 46 | } |
44 | 47 |
|
45 | | -// public.table -> FROM iceberg_scan('path') table |
46 | | -// schema.table -> FROM iceberg_scan('path') schema_table |
47 | | -func (parser *ParserTable) MakeIcebergTableNode(queryToIcebergTable QueryToIcebergTable) *pgQuery.Node { |
48 | | - node := pgQuery.MakeSimpleRangeFunctionNode([]*pgQuery.Node{ |
49 | | - pgQuery.MakeListNode([]*pgQuery.Node{ |
50 | | - pgQuery.MakeFuncCallNode( |
51 | | - []*pgQuery.Node{ |
52 | | - pgQuery.MakeStrNode("iceberg_scan"), |
53 | | - }, |
54 | | - []*pgQuery.Node{ |
55 | | - pgQuery.MakeAConstStrNode( |
56 | | - queryToIcebergTable.IcebergTablePath, |
57 | | - 0, |
58 | | - ), |
59 | | - }, |
60 | | - 0, |
61 | | - ), |
62 | | - }), |
63 | | - }) |
64 | | - |
65 | | - // DuckDB doesn't support aliases on iceberg_scan() functions, so we need to wrap it in a nested select that can have an alias |
66 | | - selectStarNode := pgQuery.MakeResTargetNodeWithVal( |
67 | | - pgQuery.MakeColumnRefNode( |
68 | | - []*pgQuery.Node{pgQuery.MakeAStarNode()}, |
69 | | - 0, |
70 | | - ), |
71 | | - 0, |
72 | | - ) |
73 | | - return parser.utils.MakeSubselectFromNode(queryToIcebergTable.QuerySchemaTable, []*pgQuery.Node{selectStarNode}, node) |
| 48 | +// public.table -> (SELECT * FROM iceberg_scan('path')) table |
| 49 | +// schema.table -> (SELECT * FROM iceberg_scan('path')) schema_table |
| 50 | +// public.table -> (SELECT permitted, columns FROM iceberg_scan('path')) table |
| 51 | +// public.table -> (SELECT NULL WHERE FALSE) table |
| 52 | +// public.table t -> (SELECT * FROM iceberg_scan('path')) t |
| 53 | +func (parser *ParserTable) MakeIcebergTableNode(queryToIcebergTable QueryToIcebergTable, permissions *map[string][]string) *pgQuery.Node { |
| 54 | + var query string |
| 55 | + if permissions == nil { |
| 56 | + query = "SELECT * FROM iceberg_scan('" + queryToIcebergTable.IcebergTablePath + "')" |
| 57 | + } else if columnNames, allowed := (*permissions)[queryToIcebergTable.QuerySchemaTable.ToIcebergSchemaTable().ToArg()]; allowed { |
| 58 | + query = "SELECT " + strings.Join(columnNames, ", ") + " FROM iceberg_scan('" + queryToIcebergTable.IcebergTablePath + "')" |
| 59 | + } else { |
| 60 | + query = "SELECT NULL WHERE FALSE" |
| 61 | + } |
| 62 | + |
| 63 | + return parser.makeSubselectNode(query, queryToIcebergTable.QuerySchemaTable) |
| 64 | +} |
| 65 | + |
| 66 | +// information_schema.tables -> (SELECT * FROM main.tables) information_schema_tables |
| 67 | +// information_schema.tables -> (SELECT * FROM main.tables WHERE table_schema || '.' || table_name IN ('permitted.table')) information_schema_tables |
| 68 | +// information_schema.tables t -> (SELECT * FROM main.tables) t |
| 69 | +func (parser *ParserTable) MakeInformationSchemaTablesNode(qSchemaTable QuerySchemaTable, permissions *map[string][]string) *pgQuery.Node { |
| 70 | + query := "SELECT * FROM main.tables" |
| 71 | + |
| 72 | + if permissions != nil { |
| 73 | + quotedSchemaTableNames := []string{} |
| 74 | + for schemaTable := range *permissions { |
| 75 | + quotedSchemaTableNames = append(quotedSchemaTableNames, "'"+schemaTable+"'") |
| 76 | + } |
| 77 | + query += " WHERE table_schema || '.' || table_name IN (" + strings.Join(quotedSchemaTableNames, ", ") + ")" |
| 78 | + } |
| 79 | + |
| 80 | + return parser.makeSubselectNode(query, qSchemaTable) |
| 81 | +} |
| 82 | + |
| 83 | +// information_schema.columns -> (SELECT * FROM main.columns) information_schema_columns |
| 84 | +// information_schema.columns -> (SELECT * FROM main.columns WHERE (table_schema || '.' || table_name IN ('permitted.table') AND column_name IN ('permitted', 'columns')) OR ...) information_schema_columns |
| 85 | +// information_schema.columns c -> (SELECT * FROM main.columns) c |
| 86 | +func (parser *ParserTable) MakeInformationSchemaColumnsNode(qSchemaTable QuerySchemaTable, permissions *map[string][]string) *pgQuery.Node { |
| 87 | + query := "SELECT * FROM main.columns" |
| 88 | + |
| 89 | + if permissions != nil { |
| 90 | + conditions := []string{} |
| 91 | + for schemaTable, columnNames := range *permissions { |
| 92 | + quotedColumnNames := []string{} |
| 93 | + for _, columnName := range columnNames { |
| 94 | + quotedColumnNames = append(quotedColumnNames, "'"+columnName+"'") |
| 95 | + } |
| 96 | + conditions = append(conditions, "(table_schema || '.' || table_name = '"+schemaTable+"' AND column_name IN ("+strings.Join(quotedColumnNames, ", ")+"))") |
| 97 | + } |
| 98 | + query += " WHERE " + strings.Join(conditions, " OR ") |
| 99 | + } |
| 100 | + |
| 101 | + return parser.makeSubselectNode(query, qSchemaTable) |
74 | 102 | } |
75 | 103 |
|
76 | 104 | func (parser *ParserTable) TopLevelSchemaFunction(rangeFunction *pgQuery.RangeFunction) *QuerySchemaFunction { |
@@ -108,3 +136,35 @@ func (parser *ParserTable) SetAliasIfNotExists(rangeFunction *pgQuery.RangeFunct |
108 | 136 |
|
109 | 137 | rangeFunction.Alias = &pgQuery.Alias{Aliasname: alias} |
110 | 138 | } |
| 139 | + |
| 140 | +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 141 | + |
| 142 | +// (query) AS qSchemaTable |
| 143 | +func (parser *ParserTable) makeSubselectNode(query string, qSchemaTable QuerySchemaTable) *pgQuery.Node { |
| 144 | + queryTree, err := pgQuery.Parse(query) |
| 145 | + common.PanicIfError(parser.config.CommonConfig, err) |
| 146 | + |
| 147 | + alias := qSchemaTable.Alias |
| 148 | + if alias == "" { |
| 149 | + if qSchemaTable.Schema == PG_SCHEMA_PUBLIC || qSchemaTable.Schema == "" { |
| 150 | + alias = qSchemaTable.Table |
| 151 | + } else { |
| 152 | + alias = qSchemaTable.Schema + "_" + qSchemaTable.Table |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + return &pgQuery.Node{ |
| 157 | + Node: &pgQuery.Node_RangeSubselect{ |
| 158 | + RangeSubselect: &pgQuery.RangeSubselect{ |
| 159 | + Subquery: &pgQuery.Node{ |
| 160 | + Node: &pgQuery.Node_SelectStmt{ |
| 161 | + SelectStmt: queryTree.Stmts[0].Stmt.GetSelectStmt(), |
| 162 | + }, |
| 163 | + }, |
| 164 | + Alias: &pgQuery.Alias{ |
| 165 | + Aliasname: alias, |
| 166 | + }, |
| 167 | + }, |
| 168 | + }, |
| 169 | + } |
| 170 | +} |
0 commit comments