77use Doctrine \DBAL \Connection ;
88use Keboola \TableBackendUtils \Column \ColumnCollection ;
99use Keboola \TableBackendUtils \Column \Snowflake \SnowflakeColumn ;
10- use Keboola \TableBackendUtils \Escaping \Exasol \ExasolQuote ;
1110use Keboola \TableBackendUtils \Escaping \Snowflake \SnowflakeQuote ;
1211use Keboola \TableBackendUtils \Table \TableDefinitionInterface ;
1312use Keboola \TableBackendUtils \Table \TableReflectionInterface ;
1413use Keboola \TableBackendUtils \Table \TableStats ;
1514use Keboola \TableBackendUtils \Table \TableStatsInterface ;
1615use Keboola \TableBackendUtils \TableNotExistsReflectionException ;
16+ use RuntimeException ;
1717use Throwable ;
1818
1919final class SnowflakeTableReflection implements TableReflectionInterface
@@ -27,53 +27,87 @@ final class SnowflakeTableReflection implements TableReflectionInterface
2727
2828 private string $ tableName ;
2929
30+ private ?bool $ isView = null ;
31+
3032 private ?bool $ isTemporary = null ;
3133
34+ private ?int $ sizeBytes = null ;
35+
36+ private ?int $ rowCount = null ;
37+
3238 public function __construct (Connection $ connection , string $ schemaName , string $ tableName )
3339 {
3440 $ this ->tableName = $ tableName ;
3541 $ this ->schemaName = $ schemaName ;
3642 $ this ->connection = $ connection ;
3743 }
3844
39- private function setIsTemporary (): bool
45+ /**
46+ * @throws TableNotExistsReflectionException
47+ */
48+ private function cacheTableProps (bool $ force = false ): void
4049 {
41- $ row = $ this ->connection ->fetchAssociative (
50+ if ($ force === false && $ this ->isView !== null && $ this ->isTemporary !== null ) {
51+ return ;
52+ }
53+ /** @var array<array{TABLE_TYPE:string,BYTES:string,ROW_COUNT:string}> $row */
54+ $ row = $ this ->connection ->fetchAllAssociative (
4255 sprintf (
43- // STARTS WITH is added because it is case-sensitive
44- 'SHOW TABLES LIKE %s IN %s STARTS WITH %s ' ,
45- SnowflakeQuote::quote ($ this ->tableName ),
46- SnowflakeQuote::quoteSingleIdentifier ($ this ->schemaName ),
56+ //phpcs:ignore
57+ 'SELECT TABLE_TYPE,BYTES,ROW_COUNT FROM information_schema.tables WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s; ' ,
58+ SnowflakeQuote::quote ($ this ->schemaName ),
4759 SnowflakeQuote::quote ($ this ->tableName )
4860 )
4961 );
50-
51- if ($ row ) {
52- return $ row ['kind ' ] === 'TEMPORARY ' ;
62+ if (count ($ row ) === 0 ) {
63+ throw TableNotExistsReflectionException::createForTable ([$ this ->schemaName , $ this ->tableName ]);
64+ }
65+ $ this ->sizeBytes = (int ) $ row [0 ]['BYTES ' ];
66+ $ this ->rowCount = (int ) $ row [0 ]['ROW_COUNT ' ];
67+ switch (strtoupper ($ row [0 ]['TABLE_TYPE ' ])) {
68+ case 'BASE TABLE ' :
69+ $ this ->isTemporary = false ;
70+ $ this ->isView = false ;
71+ return ;
72+ case 'LOCAL TEMPORARY ' :
73+ case 'TEMPORARY TABLE ' :
74+ $ this ->isTemporary = true ;
75+ $ this ->isView = false ;
76+ return ;
77+ case 'VIEW ' :
78+ $ this ->isTemporary = false ;
79+ $ this ->isView = true ;
80+ return ;
81+ default :
82+ throw new RuntimeException (sprintf (
83+ 'Table type "%s" is not known. ' ,
84+ $ row [0 ]['TABLE_TYPE ' ]
85+ ));
5386 }
54-
55- throw new TableNotExistsReflectionException ('Cannot detect if table is temporary or not. Table does not exist ' );
5687 }
5788
5889 /**
5990 * @return string[]
91+ * @throws TableNotExistsReflectionException
6092 */
6193 public function getColumnsNames (): array
6294 {
63- /** @var array<array{column_name: string}> $columnsData */
64- $ columnsData = $ this ->connection ->fetchAllAssociative (
65- sprintf (
66- // case-sensitive
67- 'SHOW COLUMNS IN %s ' ,
68- SnowflakeQuote::createQuotedIdentifierFromParts ([$ this ->schemaName , $ this ->tableName ,])
69- )
70- );
95+ $ columns = $ this ->getColumnsDefinitions ();
7196
72- return array_values (array_map (fn ($ column ) => $ column ['column_name ' ], $ columnsData ));
97+ $ names = [];
98+ /** @var SnowflakeColumn $col */
99+ foreach ($ columns as $ col ) {
100+ $ names [] = $ col ->getColumnName ();
101+ }
102+ return $ names ;
73103 }
74104
105+ /**
106+ * @throws TableNotExistsReflectionException
107+ */
75108 public function getColumnsDefinitions (): ColumnCollection
76109 {
110+ $ this ->cacheTableProps ();
77111 /** @var array<array{
78112 * name: string,
79113 * kind: string,
@@ -99,28 +133,25 @@ public function getColumnsDefinitions(): ColumnCollection
99133 return new ColumnCollection ($ columns );
100134 }
101135
102-
103-
136+ /**
137+ * @throws TableNotExistsReflectionException
138+ */
104139 public function getRowsCount (): int
105140 {
106- /** @var int|string $result */
107- $ result = $ this ->connection ->fetchOne (sprintf (
108- 'SELECT COUNT(*) AS NumberOfRows FROM %s ' ,
109- SnowflakeQuote::createQuotedIdentifierFromParts ([
110- $ this ->schemaName ,
111- $ this ->tableName ,
112- ])
113- ));
114- return (int ) $ result ;
141+ $ this ->cacheTableProps (true );
142+ assert ($ this ->rowCount !== null );
143+ return $ this ->rowCount ;
115144 }
116145
117146 /**
118147 * returns list of column names where PK is defined on
119148 *
120149 * @return string[]
150+ * @throws TableNotExistsReflectionException
121151 */
122152 public function getPrimaryKeysNames (): array
123153 {
154+ $ this ->cacheTableProps ();
124155 /** @var array<array{column_name:string}> $columnsMeta */
125156 $ columnsMeta = $ this ->connection ->fetchAllAssociative (
126157 sprintf (
@@ -132,28 +163,24 @@ public function getPrimaryKeysNames(): array
132163 return array_map (fn ($ pkRow ) => $ pkRow ['column_name ' ], $ columnsMeta );
133164 }
134165
166+ /**
167+ * @throws TableNotExistsReflectionException
168+ */
135169 public function getTableStats (): TableStatsInterface
136170 {
137- $ sql = sprintf (
138- 'SHOW TABLES LIKE %s IN SCHEMA %s STARTS WITH %s ' ,
139- ExasolQuote::quote ($ this ->tableName ),
140- ExasolQuote::quoteSingleIdentifier ($ this ->schemaName ),
141- ExasolQuote::quote ($ this ->tableName )
142- );
143- /** @var array{bytes:int|string}|null $result */
144- $ result = $ this ->connection ->fetchAssociative ($ sql );
145- if (!$ result ) {
146- throw new TableNotExistsReflectionException ('Table does not exist ' );
147- }
148-
149- return new TableStats ((int ) $ result ['bytes ' ], $ this ->getRowsCount ());
171+ $ this ->cacheTableProps (true );
172+ assert ($ this ->sizeBytes !== null );
173+ assert ($ this ->rowCount !== null );
174+ return new TableStats ($ this ->sizeBytes , $ this ->rowCount );
150175 }
151176
177+ /**
178+ * @throws TableNotExistsReflectionException
179+ */
152180 public function isTemporary (): bool
153181 {
154- if ($ this ->isTemporary === null ) {
155- $ this ->isTemporary = $ this ->setIsTemporary ();
156- }
182+ $ this ->cacheTableProps ();
183+ assert ($ this ->isTemporary !== null );
157184 return $ this ->isTemporary ;
158185 }
159186
@@ -224,14 +251,20 @@ public static function getDependentViewsForObject(
224251 * schema_name: string,
225252 * name: string
226253 * }[]
254+ * @throws TableNotExistsReflectionException
227255 */
228256 public function getDependentViews (): array
229257 {
258+ $ this ->cacheTableProps ();
230259 return self ::getDependentViewsForObject ($ this ->connection , $ this ->tableName , $ this ->schemaName , 'TABLE ' );
231260 }
232261
262+ /**
263+ * @throws TableNotExistsReflectionException
264+ */
233265 public function getTableDefinition (): TableDefinitionInterface
234266 {
267+ $ this ->cacheTableProps ();
235268 return new SnowflakeTableDefinition (
236269 $ this ->schemaName ,
237270 $ this ->tableName ,
@@ -243,18 +276,19 @@ public function getTableDefinition(): TableDefinitionInterface
243276
244277 public function exists (): bool
245278 {
246- $ row = $ this ->connection ->fetchAssociative (
247- sprintf (
248- "SELECT *
249- FROM information_schema.tables
250- WHERE TABLE_TYPE = 'BASE TABLE'
251- AND TABLE_NAME = %s AND TABLE_SCHEMA = %s
252- " ,
253- SnowflakeQuote::quote ($ this ->tableName ),
254- SnowflakeQuote::quote ($ this ->schemaName ),
255- )
256- );
279+ try {
280+ $ this ->cacheTableProps (true );
281+ } catch (TableNotExistsReflectionException $ e ) {
282+ return false ;
283+ }
284+
285+ return true ;
286+ }
257287
258- return $ row !== false ;
288+ public function isView (): bool
289+ {
290+ $ this ->cacheTableProps ();
291+ assert ($ this ->isView !== null );
292+ return $ this ->isView ;
259293 }
260294}
0 commit comments