Skip to content

Commit cf9df7a

Browse files
feat(spanner): add PG.OID support (#1948)
* feat: add PG.OID type cod annotation PiperOrigin-RevId: 577053414 Source-Link: googleapis/googleapis@727c286 Source-Link: googleapis/googleapis-gen@2015275 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiMjAxNTI3NWE3ZGRhMmFkM2QxNjA5ZjA2YzQyMDgxMjVjN2RlOGE5ZCJ9 * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * feat(spanner): add PG.OID support * Adding system tests for PG.OID * lint fixes * fixes to address review feedback --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 6ef44c3 commit cf9df7a

File tree

3 files changed

+186
-0
lines changed

3 files changed

+186
-0
lines changed

src/codec.ts

+35
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,25 @@ export class PGJsonb {
249249
}
250250
}
251251

252+
/**
253+
* @typedef PGOid
254+
* @see Spanner.pgOid
255+
*/
256+
export class PGOid extends WrappedNumber {
257+
value: string;
258+
constructor(value: string) {
259+
super();
260+
this.value = value.toString();
261+
}
262+
valueOf(): number {
263+
const num = Number(this.value);
264+
if (num > Number.MAX_SAFE_INTEGER) {
265+
throw new GoogleError(`PG.OID ${this.value} is out of bounds.`);
266+
}
267+
return num;
268+
}
269+
}
270+
252271
/**
253272
* @typedef JSONOptions
254273
* @property {boolean} [wrapNumbers=false] Indicates if the numbers should be
@@ -364,6 +383,14 @@ function decode(value: Value, type: spannerClient.spanner.v1.Type): Value {
364383
break;
365384
case spannerClient.spanner.v1.TypeCode.INT64:
366385
case 'INT64':
386+
if (
387+
type.typeAnnotation ===
388+
spannerClient.spanner.v1.TypeAnnotationCode.PG_OID ||
389+
type.typeAnnotation === 'PG_OID'
390+
) {
391+
decoded = new PGOid(decoded);
392+
break;
393+
}
367394
decoded = new Int(decoded);
368395
break;
369396
case spannerClient.spanner.v1.TypeCode.NUMERIC:
@@ -503,6 +530,7 @@ const TypeCode: {
503530
unspecified: 'TYPE_CODE_UNSPECIFIED',
504531
bool: 'BOOL',
505532
int64: 'INT64',
533+
pgOid: 'INT64',
506534
float64: 'FLOAT64',
507535
numeric: 'NUMERIC',
508536
pgNumeric: 'NUMERIC',
@@ -593,6 +621,10 @@ function getType(value: Value): Type {
593621
return {type: 'pgJsonb'};
594622
}
595623

624+
if (value instanceof PGOid) {
625+
return {type: 'pgOid'};
626+
}
627+
596628
if (is.boolean(value)) {
597629
return {type: 'bool'};
598630
}
@@ -733,6 +765,8 @@ function createTypeObject(
733765
spannerClient.spanner.v1.TypeAnnotationCode.PG_NUMERIC;
734766
} else if (friendlyType.type === 'jsonb') {
735767
type.typeAnnotation = spannerClient.spanner.v1.TypeAnnotationCode.PG_JSONB;
768+
} else if (friendlyType.type === 'pgOid') {
769+
type.typeAnnotation = spannerClient.spanner.v1.TypeAnnotationCode.PG_OID;
736770
}
737771
return type;
738772
}
@@ -748,6 +782,7 @@ export const codec = {
748782
Numeric,
749783
PGNumeric,
750784
PGJsonb,
785+
PGOid,
751786
convertFieldsToJson,
752787
decode,
753788
encode,

system-test/spanner.ts

+78
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,38 @@ describe('Spanner', () => {
827827
});
828828
});
829829

830+
describe('oids', () => {
831+
it('POSTGRESQL should read non-null pgOid values', function (done) {
832+
if (IS_EMULATOR_ENABLED) {
833+
this.skip();
834+
}
835+
PG_DATABASE.run('SELECT 123::oid', (err, rows) => {
836+
assert.ifError(err);
837+
let queriedValue = rows[0][0].value;
838+
if (rows[0][0].value) {
839+
queriedValue = rows[0][0].value.value;
840+
}
841+
assert.strictEqual(queriedValue, '123');
842+
done();
843+
});
844+
});
845+
846+
it('POSTGRESQL should read null pgOid values', function (done) {
847+
if (IS_EMULATOR_ENABLED) {
848+
this.skip();
849+
}
850+
PG_DATABASE.run('SELECT null::oid', (err, rows) => {
851+
assert.ifError(err);
852+
let queriedValue = rows[0][0].value;
853+
if (rows[0][0].value) {
854+
queriedValue = rows[0][0].value.value;
855+
}
856+
assert.strictEqual(queriedValue, null);
857+
done();
858+
});
859+
});
860+
});
861+
830862
describe('float64s', () => {
831863
const float64Insert = (done, dialect, value) => {
832864
insert({FloatValue: value}, dialect, (err, row) => {
@@ -4951,6 +4983,52 @@ describe('Spanner', () => {
49514983
});
49524984
});
49534985

4986+
describe('pgOid', () => {
4987+
const oidQuery = (done, database, query, value) => {
4988+
database.run(query, (err, rows) => {
4989+
assert.ifError(err);
4990+
let queriedValue = rows[0][0].value;
4991+
if (rows[0][0].value) {
4992+
queriedValue = rows[0][0].value.value;
4993+
}
4994+
assert.strictEqual(queriedValue, value);
4995+
done();
4996+
});
4997+
};
4998+
4999+
it('POSTGRESQL should bind the value', function (done) {
5000+
if (IS_EMULATOR_ENABLED) {
5001+
this.skip();
5002+
}
5003+
const query = {
5004+
sql: 'SELECT $1',
5005+
params: {
5006+
p1: 1234,
5007+
},
5008+
types: {
5009+
v: 'pgOid',
5010+
},
5011+
};
5012+
oidQuery(done, PG_DATABASE, query, '1234');
5013+
});
5014+
5015+
it('POSTGRESQL should allow for null values', function (done) {
5016+
if (IS_EMULATOR_ENABLED) {
5017+
this.skip();
5018+
}
5019+
const query = {
5020+
sql: 'SELECT $1',
5021+
params: {
5022+
p1: null,
5023+
},
5024+
types: {
5025+
p1: 'pgOid',
5026+
},
5027+
};
5028+
oidQuery(done, PG_DATABASE, query, null);
5029+
});
5030+
});
5031+
49545032
describe('float64', () => {
49555033
const float64Query = (done, database, query, value) => {
49565034
database.run(query, (err, rows) => {

test/codec.ts

+73
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,35 @@ describe('codec', () => {
192192
});
193193
});
194194

195+
describe('PGOid', () => {
196+
it('should stringify the value', () => {
197+
const value = 8;
198+
const oid = new codec.PGOid(value);
199+
200+
assert.strictEqual(oid.value, '8');
201+
});
202+
203+
it('should return as a number', () => {
204+
const value = 8;
205+
const oid = new codec.PGOid(value);
206+
207+
assert.strictEqual(oid.valueOf(), 8);
208+
assert.strictEqual(oid + 2, 10);
209+
});
210+
211+
it('should throw if number is out of bounds', () => {
212+
const value = '9223372036854775807';
213+
const oid = new codec.PGOid(value);
214+
215+
assert.throws(
216+
() => {
217+
oid.valueOf();
218+
},
219+
new RegExp('PG.OID ' + value + ' is out of bounds.')
220+
);
221+
});
222+
});
223+
195224
describe('Numeric', () => {
196225
it('should store value as a string', () => {
197226
const value = '8.01911';
@@ -601,6 +630,18 @@ describe('codec', () => {
601630
assert.deepStrictEqual(decoded.toString(), expected);
602631
});
603632

633+
it('should decode PG OID', () => {
634+
const value = '64';
635+
636+
const decoded = codec.decode(value, {
637+
code: google.spanner.v1.TypeCode.INT64,
638+
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_OID,
639+
});
640+
641+
assert(decoded instanceof codec.PGOid);
642+
assert.strictEqual(decoded.value, value);
643+
});
644+
604645
it('should decode TIMESTAMP', () => {
605646
const value = new Date();
606647
const expected = new PreciseDate(value.getTime());
@@ -821,6 +862,14 @@ describe('codec', () => {
821862
assert.strictEqual(encoded, '10');
822863
});
823864

865+
it('should encode PG OID', () => {
866+
const value = new codec.PGOid(10);
867+
868+
const encoded = codec.encode(value);
869+
870+
assert.strictEqual(encoded, '10');
871+
});
872+
824873
it('should encode FLOAT64', () => {
825874
const value = new codec.Float(10);
826875

@@ -986,6 +1035,12 @@ describe('codec', () => {
9861035
type: 'pgNumeric',
9871036
});
9881037
});
1038+
1039+
it('should determine if the value is a PGOid', () => {
1040+
assert.deepStrictEqual(codec.getType(new codec.PGOid(5678)), {
1041+
type: 'pgOid',
1042+
});
1043+
});
9891044
});
9901045

9911046
describe('convertToListValue', () => {
@@ -1211,5 +1266,23 @@ describe('codec', () => {
12111266
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_NUMERIC,
12121267
});
12131268
});
1269+
1270+
it('should set code and typeAnnotation for pgOid string', () => {
1271+
const type = codec.createTypeObject('pgOid');
1272+
1273+
assert.deepStrictEqual(type, {
1274+
code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.INT64],
1275+
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_OID,
1276+
});
1277+
});
1278+
1279+
it('should set code and typeAnnotation for pgOid friendlyType object', () => {
1280+
const type = codec.createTypeObject({type: 'pgOid'});
1281+
1282+
assert.deepStrictEqual(type, {
1283+
code: google.spanner.v1.TypeCode[google.spanner.v1.TypeCode.INT64],
1284+
typeAnnotation: google.spanner.v1.TypeAnnotationCode.PG_OID,
1285+
});
1286+
});
12141287
});
12151288
});

0 commit comments

Comments
 (0)